我正在查看包含以下代码的 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
}
.
.
.
正如评论中提到的,目的是“如果有多个参数,则在每个参数上运行脚本”我有一些运行程序代码来测试这一点:
# 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"
给出以下结果:
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
我希望有人能解压该test $2 && {}
部分和exit
内置的外壳。一开始我以为是test $2
在检查是否有更多参数,也许是,但它看起来很奇怪,因为似乎有两个表达式用“and”分隔&&
,我也很困惑exit
内置函数到底是做什么的。
带有示例和文档参考的简单英语解释值得赞赏:)
谢谢!
编辑:谢谢 Stéphane Chazelas。对于任何对 for 循环语法的变化感到困惑的人,请查看本文中的第 3 点极客的东西文章。总结一下:
echo "positional parameters"
for arg do
echo "$arg"
done
echo "\nin keyword"
for arg in "$@"
do echo "$arg"
done
给我们:
❯ ./for-loop-positional.sh cats and dogs
positional parameters
cats
and
dogs
in keyword
cats
and
dogs
答案1
这是一个错误的写法:
#!/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
我认为作者想要做的是检查第二个参数是否为非空字符串(这与检查是否有超过 1 个参数不同,因为第二个参数可以传递但为空字符串) 。
test somestring
一个简短的形式
test -n somestring
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 ]
或者
[ "$#" -ge 2 ]
测试变量是否非空:
[ -n "$var" ] [ "$var" != '' ] [ "$var" ]
所有这些在 的 POSIX 实现中都是可靠的
[
,但是如果您必须处理非常旧的系统,您可能必须使用[ '' != "$var" ]
相反,对于诸如, , ...
[
之类的值的实现$var
=
-t
(
测试是否定义了变量(可用于测试脚本是否传递了第二个参数,但使用
$#
更容易阅读且更惯用):[ "${var+defined}" = defined ]
(或与此形式等效的形式test
。使用命令[
的别名test
更为常见)。
cmd1 && cmd2
现在谈谈和之间的区别if cmd1; then cmd2; fi
。
cmd2
两者仅在cmd1
成功时才运行。这种情况下的区别在于,整个命令列表的退出状态将是该&&
情况下运行的最后一个命令的退出状态(因此失败代码 ifcmd1
不返回 true (尽管这里不会出错set -e
)),而在这种情况下,如果不运行,if
则将是 ofcmd2
或 0 (成功) 。cmd2
所以在cmd1
被用作状况(当其失败不被视为问题),通常最好使用它,if
特别是如果它是您在脚本中执行的最后一件事,因为它将定义脚本的退出状态。它还使代码更清晰。
该cmd1 && cmd2
形式更常用为状况他们自己喜欢:
if cmd1 && cmd2; then...
while cmd1 && cmd2; do...
这是在我们关心这两个命令的退出状态的情况下。
答案2
test $2 && some_command
由两个命令组成:
test $2
正在检查第二个参数 ( ) 扩展后的字符串是否$2
具有非零长度,即它必须检查test -n $2
([ -n $2 ]
)。请注意,由于您没有在 周围使用引号$2
,test
因此带有空格的值将会被阻塞。你应该用test "$2"
这里。&&
是一个短路求值运算符,表示&&
只有前面的命令成功(即退出代码为 0)时才会运行后面的命令。因此,在上面的示例中,只有成功(即字符串为非)some_command
时才会运行test $2
- 长度为零,然后some_command
将运行。