bash 脚本中的“test $2 &&”是什么意思?

bash 脚本中的“test $2 &&”是什么意思?

我正在查看包含以下代码的 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 ])。请注意,由于您没有在 周围使用引号$2test因此带有空格的值将会被阻塞。你应该用test "$2"这里。

  • &&是一个短路求值运算符,表示&&只有前面的命令成功(即退出代码为 0)时才会运行后面的命令。因此,在上面的示例中,只有成功(即字符串为非)some_command时才会运行test $2- 长度为零,然后some_command将运行。

相关内容