问题描述:
之前我都是用 ./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
方式执行脚本的时候,脚本需要有 r
和 x
权限。如果我们脚本的第一行使用 #!
申明了用什么 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. sh
和 bash
命令用法:
sh FileName
bash FileName
作用:在当前 bash 环境下读取并执行 FileName 中的命令。该 FileName 文件可以无执行权限
注:两者在执行文件时的不同,是分别用自己的 shell 来运行文件。
3. ./
的命令用法:
./FileName
作用:打开一个子 shell 来读取并执行 FileName 中命令。
注:运行一个 shell 脚本时会启动另一个命令解释器。