我正在查看带有以下代码的 bash 脚本:
#!/bin/sh
set -e # Exit if any command fails
# If multiple args given run this script once for each arg
test $2 && {
for arg in $@
do $0 $arg
done
exit
}
.
.
.
Run Code Online (Sandbox Code Playgroud)
正如评论中提到的,目的是“如果有多个参数,则在每个参数上运行脚本”我有一些运行器代码来测试这一点:
# install-unit
# If multiple args given run this script once for each arg
test $2 && {
echo "\$0: $0"
echo "\$1: $1"
echo "\$2: $2"
echo "test \$2 && is true"
}
test $2 && {
# note this is just a regular bash for loop
# http://goo.gl/ZHpBvT
for arg in $@
do $0 $arg
done
exit
}
echo "running: $1"
Run Code Online (Sandbox Code Playgroud)
这给出了以下内容:
sh ? ./multiple-run.sh cats and dogs and stuff
$0: ./multiple-run.sh
$1: cats
$2: and
test $2 && is true
running: cats
running: and
running: dogs
running: and
running: stuff
sh ? ./multiple-run.sh cats
running: cats
Run Code Online (Sandbox Code Playgroud)
我希望有人解开test $2 && {}零件和exit内置的外壳。一开始我想test $2是检查是否有更多的参数,也许是,但它看起来很奇怪,因为它似乎有两个表达式由 "and" 分隔&&,我也很困惑exit内置函数到底做了什么。
带有示例和文档参考的简单英语解释表示赞赏:)
谢谢!
编辑:感谢 Stéphane Chazelas。对于对 for 循环语法的变化感到困惑的任何人,请查看这篇geekstuff文章中的第3条。总结一下:
echo "positional parameters"
for arg do
echo "$arg"
done
echo "\nin keyword"
for arg in "$@"
do echo "$arg"
done
Run Code Online (Sandbox Code Playgroud)
给我们:
? ./for-loop-positional.sh cats and dogs
positional parameters
cats
and
dogs
in keyword
cats
and
dogs
Run Code Online (Sandbox Code Playgroud)
Sté*_*las 14
这是不正确的写法:
#!/bin/sh -
set -e # Exit if any command fails
# If multiple args given run this script once for each arg
if [ "$#" -gt 1 ]; then
for arg do
"$0" "$arg"
done
exit
fi
Run Code Online (Sandbox Code Playgroud)
我认为作者打算做的是检查第二个参数是否为非空字符串(这与检查是否有超过 1 个参数不同,因为第二个参数可以传递但为空字符串) .
test somestring
Run Code Online (Sandbox Code Playgroud)
一个简短的形式
test -n somestring
Run Code Online (Sandbox Code Playgroud)
如果somestring不是空字符串,则返回 true 。(test ''返回false,test anythingelse返回true(但要注意test -t某些shell 中会检查stdout 是否为终端))。
但是,作者忘记了变量周围的引号(实际上是所有变量)。这意味着 的内容$2受 split+glob 运算符的约束。因此,如果$2包含$IFS(默认为空格、制表符和换行符)或通配符(*, ?, [...])的字符,则无法正常工作。
如果$2为空(例如当传递的参数少于 2 个或第二个参数为空时),则test $2变为test,而不是test ''。test根本不接收任何参数(空或其他)。
值得庆幸的是,在这种情况下,test没有参数返回 false。它比test -n $2返回 true的情况略好(因为它会变成test -n,与 相同test -n -n),因此该代码在某些情况下似乎可以工作。
总结:
测试是否传递了 2 个或更多参数:
[ "$#" -gt 1 ]
Run Code Online (Sandbox Code Playgroud)
或者
[ "$#" -ge 2 ]
Run Code Online (Sandbox Code Playgroud)测试变量是否为非空:
[ -n "$var" ]
[ "$var" != '' ]
[ "$var" ]
Run Code Online (Sandbox Code Playgroud)
所有这些在 的 POSIX 实现中都是可靠的[,但是如果您必须处理非常旧的系统,则可能必须使用
[ '' != "$var" ]
Run Code Online (Sandbox Code Playgroud)
而不是[在$varlike =, -t, (...
测试是否定义了变量(可用于测试脚本是否传递了第二个参数,但使用$#更容易阅读和更惯用):
[ "${var+defined}" = defined ]
Run Code Online (Sandbox Code Playgroud)(或等价的test形式。[为test命令使用别名更为常见)。
现在谈谈cmd1 && cmd2和之间的区别if cmd1; then cmd2; fi。
两者cmd2仅在cmd1成功时运行。在这种情况下的区别在于,整个命令列表的退出状态将是在这种&&情况下运行的最后一个命令的退出状态(因此失败代码如果cmd1不返回真(尽管set -e这里不会跳闸))而在在这种if情况下,cmd2如果cmd2不运行,则为 或 0(成功)。
因此,在将 wherecmd1用作条件的情况下(当它的失败不被视为问题时),通常最好使用if它,特别是如果它是您在脚本中做的最后一件事,因为这将定义您的脚本的退出状态。它还使代码更清晰。
该cmd1 && cmd2形式更常用作条件本身,例如:
if cmd1 && cmd2; then...
while cmd1 && cmd2; do...
Run Code Online (Sandbox Code Playgroud)
那是在我们关心这两个命令的退出状态的上下文中。
test $2 && some_command 由两个命令组成:
test $2正在检查第二个参数 ( $2)展开后的字符串是否具有非零长度,即它必须检查test -n $2( [ -n $2 ])。请注意,由于您没有在 周围使用引号$2,因此test会阻塞带有空格的值。你应该test "$2"在这里使用。
&&是一个短路求值操作符,表示后面的命令&&只有在它之前的命令成功时才会运行,即退出代码为0。所以在上面的例子中,some_command只有在test $2成功时才会运行,即如果字符串是非-零长度,然后some_command将运行。