一个神奇的错误引出的 Shell 中 source、sh、bash、./ 执行脚本的区别

问题描述:

之前我都是用 ./test.sh 方式执行脚本的,但是后来我看到有 sh test.sh 方式执行脚本,而且这种方式执行脚本的时候,脚本可以不用执行权限。

在此前提下,我执行一个 Flink 脚本的时候突然报语法错误,我用的是 sh bin/config.sh 执行的脚本。
报错信息如下:

bin/config.sh: line 32: syntax error near unexpected token `<'
bin/config.sh: line 32: `    done < <(find "$FLINK_LIB_DIR" ! -type d -name '*.jar' -print0 | sort -z)'

config.sh 文件部分代码:

#!/usr/bin/env bash
################################################################################
#  Licensed to the Apache Software Foundation (ASF) under one
#  or more contributor license agreements.  See the NOTICE file
#  distributed with this work for additional information
#  regarding copyright ownership.  The ASF licenses this file
#  to you under the Apache License, Version 2.0 (the
#  "License"); you may not use this file except in compliance
#  with the License.  You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
#  Unless required by applicable law or agreed to in writing, software
#  distributed under the License is distributed on an "AS IS" BASIS,
#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#  See the License for the specific language governing permissions and
# limitations under the License.
################################################################################

constructFlinkClassPath() {
    local FLINK_DIST
    local FLINK_CLASSPATH

    while read -d '' -r jarfile ; do
        if [[ "$jarfile" =~ .*flink-dist.*.jar ]]; then
            FLINK_DIST="$FLINK_DIST":"$jarfile"
        elif [[ "$FLINK_CLASSPATH" == "" ]]; then
            FLINK_CLASSPATH="$jarfile";
        else
            FLINK_CLASSPATH="$FLINK_CLASSPATH":"$jarfile"
        fi
    done < <(find "$FLINK_LIB_DIR" ! -type d -name '*.jar' -print0 | sort -z)

    if [[ "$FLINK_DIST" == "" ]]; then
        # write error message to stderr since stdout is stored as the classpath
        (>&2 echo "[ERROR] Flink distribution jar not found in $FLINK_LIB_DIR.")

        # exit function with empty classpath to force process failure
        exit 1
    fi

    echo "$FLINK_CLASSPATH""$FLINK_DIST"
}

发现问题:

由错误信息可知,我们错误是在第 32 行,报语法错误。这就奇怪了,我之前运行这个脚本都没问题,为什么现在突然报语法错误,我也没修改这个脚本文件啊。然后我叫同事试了下,他执行是没问题的。

然后我一脸懵逼,难道是我人品不好?后面发现他是用 ./bin/config.sh 执行脚本的,我是用 sh bin/config.sh 执行脚本的。区别就在这里,运行脚本的方式不同。

于是我发现了问题所在,那就要找为什么这两种执行脚本的方式会产生截然不同的结果。

为什么会导致这个问题的出现:

当我们用 ./bin/config.sh 方式执行脚本的时候,脚本需要有 rx 权限。如果我们脚本的第一行使用 #! 申明了用什么 shell 执行脚本,那么 ./ 方式就会用申明的方式执行脚本,否则用默认的方式执行,一般是 bash

当我们用 sh bin/config.sh 方式执行脚本的时候,脚本不需要任何权限都可以执行。执行的时候是用 sh 执行的,也就是不管你在脚本第一行申明的是什么方式执行脚本都没用。

所以,现在我们知道了 ./bin/config.sh 方式是使用 bash 方式执行了脚本;而sh ./bin/config.sh 是用的 sh 方式执行的脚本。也就是说第 32 行的语法在 sh 中是不支持的,会报语法错误;但是在 bash 中是没问题的。

解决方法:

现在我们知道了必须得用 bash 执行脚本才不会报错,所以我们可以用 ./bin/config.sh 或者 bash bin/config.sh 两种方式执行脚本。

由问题引申出的知识:

执行脚本的方式:

在 shell 中执行一个脚本有 source, sh, bash, ./

1. source 命令的使用方法:
source FileName

作用:在当前 bash 环境下读取并执行 FileName 中的命令。该 FileName 文件可以无执行权限
注:该命令通常用命令 . 来替代。

如:source .bash_profile. .bash_profile 两者等效。

  • source. 命令通常用于重新执行刚修改的初始化文档。
  • source 命令(从 C Shell 而来)是 Bash Shell 的内置命令。
  • . 命令,就是个点符号,(从 Bourne Shell 而来)。
2. shbash 命令用法:
sh FileName

bash FileName

作用:在当前 bash 环境下读取并执行 FileName 中的命令。该 FileName 文件可以无执行权限
注:两者在执行文件时的不同,是分别用自己的 shell 来运行文件。

3. ./的命令用法:
./FileName

作用:打开一个子 shell 来读取并执行 FileName 中命令。

注:运行一个 shell 脚本时会启动另一个命令解释器。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值