我对这种行为感到困惑,我有以下脚本:
backup.sh
#!/bin/bash -x
set -e
if [[ $# -eq 0 ]] ; then
echo 'No arguments passed'
exit 1
fi
# Get the arguments
for ARGUMENT in "$@"; do
KEY=$(echo $ARGUMENT | cut -f1 -d=)
VALUE=$(echo $ARGUMENT | cut -f2 -d=)
case "$KEY" in
backup_dir) BACKUP_DIR=${VALUE} ;;
postgres_dbs) POSTGRES_DBS=${VALUE} ;;
backup_name) BACKUP_NAME=${VALUE} ;;
postgres_port) POSTGRES_PORT=${VALUE} ;;
postgres_host) POSTGRES_HOST=${VALUE} ;;
*) ;;
esac
done
我正在使用以下命令执行它:
1.
/bin/bash -c /usr/bin/backup.sh postgres_dbs=grafana,keycloak backup_name=postgres-component-test-20220210.165630 backup_dir=/backups/postgres postgres_port=5432 postgres_host=postgres.default.svc.cluster.local
/usr/bin/backup.sh postgres_dbs=grafana,keycloak backup_name=postgres-component-test-20220210.165630 backup_dir=/backups/postgres postgres_port=5432 postgres_host=postgres.default.svc.cluster.local
但输出是:
+ set -e
+ [[ 0 -eq 0 ]]
+ echo 'No arguments passed'
No arguments passed
+ exit 1
环境:
# cat /etc/os-release
NAME="Ubuntu"
VERSION="18.04.3 LTS (Bionic Beaver)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 18.04.3 LTS"
VERSION_ID="18.04"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=bionic
UBUNTU_CODENAME=bionic
我可以重现此问题的 Bash 版本:
GNU bash, version 4.4.20(1)-release (x86_64-pc-linux-gnu)
但是,这在 Bash 版本中不会发生:
GNU bash, version 5.1.8(1)-release (x86_64-apple-darwin20.3.0)
这不是一个错误,只是一个功能!
当您使用bash -c 'code …'
样式时,实际上第一个 CLI 参数作为$0
传递给内联代码,而不是$1
。
此外,如果'code …'
本身调用外部脚本,例如./script.sh
,那么您不应忘记使用"$@"
构造传递参数。
所以你可以写(如评论中指出的那样):
bash -c './script.sh "$@"' bash "first argument"
或者最简洁地说,就像你提到你已经尝试过一样:
bash script.sh "first argument"
其他说明
由于您的示例并不是真正的"最小"(它有一个很长的命令行),因此您可能希望出于调试目的对其进行测试的完整最小示例:
script.sh
#!/usr/bin/env bash
echo "$#: $#"
for arg; do printf -- '- %sn' "$arg"; done
然后,您应该获得类似于以下内容的会话:
$ chmod a+x script.sh
$ bash -c ./script.sh "arg 1" "arg 2"
$#: 0
$ bash -c './script.sh "$@"' "arg 1" "arg 2"
$#: 1
- arg 2
$ bash -c './script.sh "$@"' bash "arg 1" "arg 2"
$#: 2
- arg 1
- arg 2
$ bash script.sh "arg 1" "arg 2"
$#: 2
- arg 1
- arg 2
$ ./script.sh "arg 1" "arg 2"
$#: 2
- arg 1
- arg 2
您编写了两种调用脚本的方法,归结为:
bash -c ./script.sh arg1 arg2 arg3
./script.sh arg1 arg2 arg3
第二种方法是调用脚本的首选方法。直接运行它们会告诉 Linux 使用舍邦行中列出的解释器。我没有理由看到这种调用风格会删除参数。
然而,第一个确实失去了所有的论点。这是因为-c
不属于那里。如果你想调用一个显式的bash
shell,你应该简单地写:
bash ./script.sh arg1 arg2 arg3
这将正确地将所有参数传递给脚本。
当您添加-c
时,它会./script.sh
从脚本的名称变成一个完整的命令行。有什么区别?好吧,现在命令行负责将其参数转发到脚本,如果这是它想要发生的事情。使用-c
您需要显式传递它们:
bash -c './script.sh "$@"' bash arg1 arg2 arg3
呸!它被包裹在单引号中,里面有一个丑陋的"$@"
。不过,这是需要的。没有"$@"
,论点就被简单地扔在地板上。
-c
还需要一个额外的参数,即$0
的值。因此,不仅需要"$@"
,还必须添加额外的bash
参数来设置$0
。(bash
是一个不错的选择,因为这是运行 bash 脚本时通常$0
设置的。