如何在Bash脚本中将可选参数传递给另一个命令?

9
我是一个帮助翻译文本的助手。

我正在编写一个bash脚本,它接受一些可选参数。我想将它们翻译并传递给另一个脚本。然而,我很难优雅地传递这些可选参数。

以下是我用伪代码成功实现的大纲:

a.sh:

if arg1 in arguments; then
    firstArg="first argument"
fi
if arg2 in arguments; then
    secondArg="second argument"
fi

./b.sh $firstArg $secondArg "default argument"

请注意参数中的空格。
b.sh:
for arg in "$@"
do
    echo $arg
done

我希望调用b.sh,可以选择传入 firstArgsecondArg ,并且可以有一个默认参数,如下所示:

./b.sh $firstArg $secondArg "default argument"

这样做的问题在于,如果$firstArg$secondArg是带有空格的字符串,它们将被表示为多个参数,并且输出将类似于:
first
argument
second
argument
default argument

好的,这很容易解决,我们可以通过在参数周围添加引号来捕获整个字符串,像这样:

./b.sh "$firstArg" "$secondArg" "defaultArg"

问题在于,如果例如firstArg未设置,则会导致输出空行(因为它将解释""作为参数),因此输出将类似于:

(blank line here)
second argument
defaultArg

我也尝试过构建字符串并将其传递给Shell脚本,但似乎这种方式也不起作用(即使我使用引号分隔参数,它仍将整个字符串解释为一个参数)。

请注意,在命令行中使用带引号的参数调用b.sh可以正常工作。是否有一种方法可以在Bash脚本中模仿这种工作方式?


1
附言:仅仅因为我在模仿你的做法,使用“.sh”扩展名来命名可执行文件(即那些具有+x权限且可以通过execv()系列调用成功的文件),并不意味着这是一个好主意。一般来说,在UNIX中,无论是通过shell脚本、Python脚本、编译后的二进制文件等方式提供的命令都不应该有任何扩展名。 - Charles Duffy
您能明确指出这是用于#!/bin/sh还是#!/bin/bash的代码吗? - Charles Duffy
2个回答

9
如果你想要逐字复制所有给定的参数,但是又想增加一个参数:
# this works in any POSIX shell
./b.sh "$@" defaultArg

或者,如果它们存在的话(请注意,将其设置为空值也算“存在”),可以显式传递firstArgsecondArg

# this works in any POSIX shell
./b.sh ${firstArg+"$firstArg"} ${secondArg+"$secondArg"} defaultArg

如果你想将设置为空值视为不存在:
# this works in any POSIX shell
./b.sh ${firstArg:+"$firstArg"} ${secondArg:+"$secondArg"} defaultArg

另一种方法是构建参数数组:

# this requires bash or another shell with arrays and append syntax
# be sure any script using this starts with #!/bin/bash
b_args=( )
[[ $firstArg ]] && b_args+=( "$firstArg" )
[[ $secondArg ]] && b_args+=( "$secondArg" )
b_args+=( "default argument" )
./b.sh "${b_args[@]}"

如果你想要与数组方法相同的灵活性,但不想有兼容性问题,可以定义一个函数,在其中安全地覆盖"$@",而不会影响脚本的其他部分:
runB() {
  set --
  [ -n "$firstArg" ]  && set -- "$@" "$firstArg"
  [ -n "$secondArg" ] && set -- "$@" "$secondArg"
  ./b.sh "$@" "default argument"
}

这里引用 "$@" 不是有问题吗?这样做之后它不再被分割成单词了,对吧? - hek2mgl
好答案!我在发表评论时没有正确理解问题。 - hek2mgl
我只是还没有尝试过,我会试一下看看是否可行。 - Andrew
顺便提一下,如果您确实需要与具有数组但不支持附加的bash兼容(例如2.x系列中的某些内容),那么b_args+=(“$firstArg”)将变得不太高效且更冗长,而应改为b_args=(“$ {b_args [@]}”“$firstArg”) - Charles Duffy
我可以确认数组方法是有效的。感谢Charles和其他任何回答过我的人提供的所有帮助。你们似乎都是真正的shell脚本大师! - Andrew
显示剩余2条评论

1
使用一个数组:
args=()

if [ ... ]; then
    args+=( "first argument" )
fi

if [ ... ]; then
    args+=( "second argument" )
fi

./b.sh "${args[@]}" "default argument"

@EtanReisner 不会,因为在 OP 的例子中 $firstArg 可能根本没有被设置(但被使用),但如果它确实被设置了,它将被设置为一个字符串字面量(与上面的片段相同)。 - Adrian Frühwirth
啊,我以为那些是实际第一和第二参数的占位符,而不是字面字符串(在OP和这里都是如此),由于您省略了对参数本身的实际测试,所以我没有填写那些内容。按照现有的写法,这个问题已经解决了,但是按照现有的写法,这也只是解决方案的一半(我认为并没有完全正确地解决它)。 - Etan Reisner
我理解你这方面的想法。然而,原帖提供了 ./b.sh "$firstArg" "$secondArg" "defaultArg" 作为一种可能的解决方案(并且仅将其唯一的缺点描述为传递空参数而不是根本未提供它们),这表明在问题早期使用文字串 "first argument""second argument" 只是粗心大意。 - Charles Duffy
@CharlesDuffy 我明白了,我承认我可能没有完全理解问题,但这只是一个关于if语句如何构建的问题,因为你也建议这样做。除非我还是漏掉了什么 :-) - Adrian Frühwirth

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接