为什么不对||使用多个命令 或&&有条件的工作?


12

这适用于shell(重击,破折号)提示:

[ -z "" ] && echo A || echo B
A

但是,我试图编写一个POSIX shell脚本,它的开始是这样的:

#!/bin/sh

[ "${#}" -eq 1 ] || echo "Invalid number of arguments, expected one."; exit 1

readonly raw_input_string=${1}

[ -z "${raw_input_string}" ] && echo "The given argument is empty."; exit 1

而且我不知道为什么,但是我没有得到消息

给定的参数为空。

如果我这样调用脚本:

./test_empty_argument ""

这是为什么?


5
请参阅如何测试变量是否为空或仅包含空格?用于测试变量是否为空,未设置或仅包含空格的方法。这个问题的问题与此无关。
ilkkachu

1
只需使用if [ X”” = X”$var” ] ; then echo isempty ; fi
user2497 '19

3
@ user2497没有理由在最近20年发布的任何shell中使用它。这是旧的有问题的外壳的解决方法。
chepner

@chepner所以这不是一个有效的解决方案?还必须使用其他东西吗?
user2497 '19

6
[ "" = "$var" ]会很好;带引号的空字符串不会从的参数列表中删除[。但这也不是必须的,因为[ -z "$var" ] 可以。
chepner

Answers:


37

注意你的行

[ "${#}" -eq 1 ] || echo "Invalid number of arguments, expected one."; exit 1

这和

[ "${#}" -eq 1 ] || echo "Invalid number of arguments, expected one."
exit 1

;在大多数情况下,可以用换行符代替不带引号的字符)

这意味着exit 1无论将多少参数传递给脚本,该语句始终会执行。反过来,这意味着该消息The given argument is empty.将永远不会有被打印的机会。

要在使用“短路语法”进行测试后执行多个语句,请将这些语句分组在中{ ...; }。另一种方法是使用适当的if语句(恕我直言,在脚本中看起来更清晰):

if [ "$#" -ne 1 ]; then
    echo 'Invalid number of arguments, expected one.' >&2
    exit 1
fi

您的第二项测试存在相同的问题。


关于

[ -z "" ] && echo A || echo B

对于给定的示例,这将起作用,但是通用的

some-test && command1 || command2

不会是相同的

if some-test; then
    command1
else
    command2
fi

相反,它更像

if ! { some-test && command1; }; then
    command2
fi

要么

if some-test && command1; then
    :
else
    command2
fi

也就是说,如果测试或第一个命令失败,则执行第二个命令,这意味着它有可能执行所有三个涉及的语句。


18

这个:

[ "${#}" -eq 1 ] || echo "Invalid number of arguments, expected one."; exit 1

不是:

[ "${#}" -eq 1 ] || { echo "Invalid number of arguments, expected one."; exit 1; }

而是:

{ [ "${#}" -eq 1 ] || echo "Invalid number of arguments, expected one."; } 
exit 1

无论您传递了多少参数,脚本都将退出。


8

使其更具可读性的一种方法是定义一个die函数(àla perl),例如:

die() {
  printf >&2 '%s\n' "$@"
  exit 1
}

# then:

[ "$#" -eq 1 ] || die "Expected one argument, got $#"

[ -n "$1" ] || die "Empty argument not supported"

您可以根据需要添加更多的钟声,例如颜色,前缀,行号。


实际上,您是否曾经die使用多个参数调用函数?(如果可以,您可以举个例子吗?)我使用的die功能几乎相同,但是使用的"$*"却是,您打算使用的功能更多?
jrw32982支持Monica19年

3
其价值"$@"在于它允许多行消息,而无需添加文字换行符。
查尔斯·达菲

1
@ jrw32982,"$*"用于将args与空格连接起来还意味着您需要将其设置$IFS为SPC才能在所有上下文中工作,包括$IFS已修改的上下文。或者用ksh/ zsh,您可以使用print -r -- "$@"echo -E - "$@"zsh
斯特凡Chazelas

@CharlesDuffy是的,但我从未die-type函数的上下文中看到它。我要问的是:在实践中,您是否见过有人写die "unable to blah:" "some error",目的是获得两行错误消息?
jrw32982支持Monica19年

@StéphaneChazelas好点。因此(按照我的说法)应该是die() { IFS=" "; printf >&2 "%s\n" "$*"; exit 1; }。您是否曾经亲自使用这种die函数printf通过传递多个参数来生成多行错误消息?还是只向传递一个参数die,使其仅向输出添加一个换行符?
jrw32982支持Monica19年

-1

我经常将其视为对空字符串的测试:

if [ "x$foo" = "x" ]; then ...

应该是“ =”-已修复。

3
这种做法实际上是从1970年代开始的。没有任何理由用与1992年的POSIX SH标准的规定(任何外壳使用它,只要正确使用,现在,过时的功能,如引用-a-o()作为derectives告诉test到多个操作可在单个调用结合避免;请参见pubs.opengroup.org/onlinepubs/9699919799/utilities/test.html中的OB标记)。
查尔斯·达菲

原始的“ sh”附带的非Linux unice很好地进入了90年代-也许现在仍然如此。在当时的工作中,我不得不编写可移植的安装脚本,并且使用了这种结构。我只是看了NVidia为Linux提供的安装脚本,他们仍然使用此构造。

NVidia可以使用它,但这并不是说他们有任何技术理由;令人遗憾的是,在商业UNIX中,无赖开发大量的软件。甚至Heirloom Bourne都没有问题的代码,因此SunOS代码库(这是最后一个发布非POSIX的商业UNIX /bin/sh,而Heirloom Bourne的直接前身)也没有问题。
查尔斯·达菲,

1
我没有戴帽子,所以我不能保证如果有人用1990年后的这个错误打开在商业UNIX上发布的外壳,就不会发布一个YouTube视频,但是如果我这样做了,那将很诱人。:)
Charles Duffy
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.