“ / bin / [”究竟如何工作?


50

我总是很惊讶文件夹中/bin有一个[程序。

当我们做类似的事情时,这就是所谓的if [ something ]吗?

通过[在shell中显式调用该程序,它会要求一个对应的],当我提供右括号时,无论我在括号之间插入什么,它似乎都无济于事。

不用说,有关帮助有关程序不能正常工作,即无论是通常的方式man [,也没有[ --help工作。


11
@IporSircer 虽然不是[test命令expr,所以应该是man test
Sergiy Kolodyazhnyy

8
man '['对我来说效果很好-您忘了引用[或者您对“作品”有不同的定义。
Toby Speight

3
一些警告:[评估其参数,因此您需要在所有参数之间留有空格。 [ a=b ]不是比较:它将始终为true(它是一个字符串:“ a = b”,始终被评估为true),并且应将参数的数量限制为4(即使最近的实现允许更多。) 。限制为4使其更具可移植性。例如:[ "a" = "b" ]已经有4个参数:“ a”“ =” =“ b”和test arg的不必要结尾:“]”)。如果您需要更多:链测试,例如:if [ "$Var_a" = "foo" ] && [ "$Var_b" = "bar" ] ; then : do something ; fi
Olivier Dulac

1
@OlivierDulac !本身(未转义和未引用)不会被替换,甚至更好if ! [ ...。使用的所有bash扩展!至少都包含其他字符
muru

3
您还应该注意,还[内置了-builtin bash(也可能还有其他shell),并且可以使用-buildin 代替/bin/[。还有test-command,在许多系统上,它是至/bin/[(或反之)的符号链接-在其他系统上,则是单独的命令。
巴尔德·科珀罗德

Answers:


63

[命令的工作是评估测试表达式。当表达式解析为true时,它将返回0退出状态(表示true),否则返回其他状态(表示false)。

这并不是说它什么也不做,只是它的结果可以在退出状态中找到。在shell中,您可以找到有关$?Bourne类shell或$status其他大多数shell(fish / rc / es / csh / tcsh ...)中最后一个命令的退出状态。

$ [ a = a ]
$ echo "$?"
0
$ [ a = b ]
$ echo "$?"
1

在诸如之类的其他语言中perl,退出状态例如以返回值返回system()

$ perl -le 'print system("[", "a", "=", "a", "]")'
0

请注意,所有现代的类似于Bourne的shell(和fish)都具有内置[命令。/bin通常仅当您使用另一个shell或执行类似env [ foo = bar ]find . -exec [ -f {} ] \; -print或该perl命令的操作时才执行in ...

[命令也被称为test名称。称为时test,不需要结束]参数。

虽然您的系统可能没有用于的手册页[,但可能会有用于的手册页test。但是再次注意,它将记录/bin/[/bin/test实现。要了解[外壳程序中的内置功能,您应该阅读外壳程序的文档。

有关该实用程序的历史以及与[[...]]ksh测试表达式的区别的更多信息,您可能希望在此处查看其他问答


和往常一样好。您可能要添加我在@Bregalad问题下添加的评论?还是一些额外的?这样,答案将在“它是如何工作的”上更加完整(我注意到,您已经提供了比我更准确的“]”信息)^
Olivier Dulac

“所有类似Bourne的贝壳(和鱼)都有内置的[命令”。这在21世纪的实践中是正确的,但是我敢肯定,我使用的贝壳没有[。我不记得它是伯恩(Bourne)的变种还是灰烬的古董版本。
吉尔斯(Gillles)“所以-别再作恶了”

@Gilles伯恩壳实现既[test与Unix的系统III建宏。在此之前,没有[而且test只有一个外部二进制命令。
jlliagre

@Gilles,原始灰烬已经过测试内置(与expr合并),但是可选。根据in-ulm.de/~mascheck/various/ash的说法,BSD直到2000年左右才进行内置测试。我添加了“ modern”。
斯特凡Chazelas

为什么要在世界上做find . -exec [ f {} ] \; -print什么?该命令find . -type f -print会做同样的事情,所以更有效地...
这不是我的真名

41

我总是很惊讶文件夹中/bin有一个[程序。

您感到惊讶是正确的。这是使用NULL实用程序(:)的罕见POSIX命令之一,它不遵守命令文件允许的字符约定(便携式文件名字符集)。

当我们做类似的事情时,被称为什么if [ something ]

精确,但是也可以不使用它if

通过[在shell中显式调用该程序,它会要求一个对应的],当我提供右括号时,无论我在括号之间插入什么,它似乎都无济于事。

它不显示任何内容,但实际上与使用时的功能完全相同if,即根据您放在方括号中的内容将退出状态设置为0(真)或其他任何值(假)。(出于某种原因)它与test命令具有相同的行为。唯一的区别是它寻找结尾]。有关man test详细信息,请参见。

不用说,有关帮助有关程序不能正常工作,即无论是通常的方式man [,也没有[ --help工作。

这取决于您的操作系统。man [在几个主流的Gnu / Linux发行版上肯定可以为我工作,但在Solaris上则不能。

[ --help可能会根据实现而起作用,因为它会破坏语法,但会丢失end ]。此外,对于POSIX标准test/ [命令明确排除了所有选项,包括--选项终止这样既[ --help ]test --help需要返回true,并通过设计沉默。需要注意的是你把括号内或之后[,并且看起来像选项(例如-f file-n string和喜欢)不是选项,但操作数

所有现代风格的Bourne外壳口译(如bashkshdashzsh仅举几例)执行test/ [效用内部作为一个内置的,所以当你使用它们,正确的手册页指可能是外壳的一个,而不是test一个。

在Unix System III(1981)之前,Bourne shell并未将test实用程序作为内置程序来实现,因此只能使用外部二进制命令实现。[直到Unix System III 都没有(内部或内置)命令,因此例如在Unix Version 7下,您必须输入:

if test something ; then

代替:

if [ something ] ; then

“人[在主流的Gnu / Linux发行版上肯定对我有用” Hmm .. Centos(公认的仍然是6.8)显然还不够主流:), man test但是还没有man [ ,另一方面,我的cygwin环境确实众所周知man [
亚当

1
@Adam是的,您是对的,尽管我没有编写所有主流Linux发行版,但它比我想象的要新,只是它对我有用(即在我测试过的发行版中)。那就是OL 7.2(RHEL 7.2)克隆和Mint 17.1(Ubuntu 14.04)。
jlliagre

man [在Manjaro(基于Arch Linux)上似乎不起作用。test清单手册[ --help是有效的调用,应该显示帮助,但有趣的是,它没有,而是抱怨缺少],与相同--version。添加]不会执行任何操作,因此从寻求帮助似乎是不可能的man test
Darkhogg

@Darkhogg您可以尝试/usr/bin/[ --help/bin/[ --help
jlliagre

1
@jlliagre您完全正确,我正在使用内置函数。env [ --help以及/usr/bin/[ --help工作完全正常(尽管我需要引用/usr/bin/[或zsh抱怨)。
Darkhogg

10

[实际上更通常称为test命令。此命令的典型用法是简单地计算表达式并返回其条件-true或false。它通常用在if-then-else-fi语句中,尽管可以在语句外部使用它,以便像这样if通过外壳程序&&||运算符有条件地运行其他命令。

$ [ -e /etc/passwd  ] && echo "File exists"
File exists

$ test -e /etc/passwd && echo "File exists"
File exists

更具体地说,评估是通过退出状态传达给其他命令的。某些程序可能选择输出退出状态以表示不同类型的事件-程序成功完成,在执行期间发生特定类型的错误或语法错误。在该test命令的情况下,0表示真,1表示假。正如Stephan所指出的那样,语法错误会产生的退出状态2

它的位置取决于您的系统,并且还说明了为什么您看不到手册页的原因man [。例如,在FreeBSD上,它位于下/bin。在Linux上(或在我的特定情况下为Ubuntu 16.04),它在中/usr/bin/。如果您这样做man [man test在Linux系统上,则将看到相同的文档。还需要注意的是,您的shell可能有其自己的实现test

还应注意,此命令存在一些问题,Korn shell的实现(通常称为带有双方括号的“条件表达式”引用[[ "$USER" = "root" ]])试图解决。其他外壳程序(例如bash和)也使用此功能zsh


/usr/bin/也适用于Amazon Linux。
franklinsijo

@StéphaneChazelas谢谢。答案因此编辑
谢尔盖Kolodyazhnyy

3

既不man [也不[ --help作品

在某些发行版(例如Ubuntu)上,man [跟随到的符号链接test(1)。在其他情况下(例如Arch),情况并非如此。(但test手册页也记录了用法[


type -a [ 向您展示了既有内置的Shell又有可执行文件。

$ type -a [
[ is a shell builtin
[ is /usr/bin/[

bash-builtin [ --help仅显示错误消息。(但是,由于它是内置函数,因此您可以使用help [或查看bash手册页/ docs)。

/usr/bin/[ --help 打印完整的帮助输出(对于GNU Coreutils版本),其输出以:

$ /usr/bin/[ --help
Usage: test EXPRESSION
  or:  test
  or:  [ EXPRESSION ]
  or:  [ ]
  or:  [ OPTION
Exit with the status determined by EXPRESSION.

      --help     display this help and exit
      --version  output version information and exit

然后描述EXPRESSION允许使用的语法。

这是你可能已经发现了另一种方式[test是等价的。


顺便说一句,如果您正在为bash编程(或以交互方式编写单行代码),则建议您使用[[而不是[。在几种方面更好,请参见Serg答案中的链接。


2

[如果包含在其参数中的表达式为真,则命令返回exit-status零;如果包含在其参数中的表达式为false,则返回非零退出状态。如果没有最后一个参数,它也会失败并显示错误消息](这纯粹是出于审美原因)。

例如:

[ hello ]
echo "Exit-status of [ hello ] is:" $?
[ abc = abc ]
echo "Exit-status of [ abc = abc ] is:" $?
[ ]
echo "Exit-status of [ ] is:" $?
[ abc = def ]
echo "Exit-status of [ abc = def ] is:" $?

…将输出:

[hello]的退出状态为:0-       因为认为非空字符串为真 
。[abc = abc]的退出状态为:0-   因为'abc'确实与'abc'相同 
。[] 退出状态为:1-             因为认为空字符串为假 
[abc = def]的退出状态为:1-   因为'abc'与'def'确实不同

但是,在这些情况下,bash和许多其他shell实际上通常不会调用/bin/[(或/usr/bin/[),而是以完全相同的行为调用内置命令(纯粹出于性能原因)。要调用/bin/[(不是shell内置代理)(您需要显式指定其路径(例如/bin/[ hello ];无需]通过name 前缀dirname)),或者将shell配置为不使用内置代理(例如,enable -n [在bash中)。

PS:正如在其他答案中所说,[与有关test。但是test,不像[,它不需要]作为最后一个参数(并且根本不期望它;]test参数中添加额外内容可能导致它失败,并显示错误消息或返回错误结果)/bin/test/bin/[可以解析为同一个文件(例如,一个是符号链接 ;在这种情况下,行为转移被执行大概分析了目前所谓的命令的内test/ [代码本身),或者以不同的文件。对于test,shell通常也调用内置代理,除非明确指定了路径(/bin/test)或配置为不指定路径(enable -n test)。

PPS:与test和不同[,现代if从来都不是真实文件。它是shell(例如bash)语法的一部分:(if commandA; then commandB; fi可以使用换行符代替分号)commandB,如果仅以commandA零状态退出则导致执行。这非常适合testor的行为[,允许将它们组合在一起if [ "$a" = foo ]; then …; fi (或if test "$a" = foo; then …; fi-可读性较低)。但是,现代脚本经常使用[[代替test[,后者(作为if)从来不是真正的文件,而始终是shell语法的一部分。

PPPS:至于man-永远不要期望man在文件系统中的每个命令上都有一篇文章。一些(甚至是“真实”的,基于文件的)命令的信息可能会丢失,在某些shell内置也许现在不仅在专用于特定外壳的一篇文章(这就是你肯定会发现信息的地方信息test[if[[)。但是,许多发行版中都有明确man的文章test[。(关于--helptest由于明显的原因而未被识别:它需要安静地处理类似a=--help; test "$a";的情况;在某些发行版中[ --help(未关闭])仍然显示出帮助,而在某些情况下则没有帮助。)


3
请注意,这if是Unix V6之前的命令。Unix V7(1979)带有Bourne shell(及其if-then-else-fi构造)和新test命令。
斯特凡Chazelas
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.