测试你的方括号
Test your square brackets

原始链接: https://fluca1978.github.io/2025/12/10/testAndSquareBrackets.html

## Shell脚本中 `test` 和 `[` 的奇妙之处 许多Shell脚本编写者习惯使用 `[ ... ]` 进行条件测试,常常忘记它的起源。最初,`test` 命令是执行这些检查的标准方式——`if test -f "$file" then...fi`。`[` 语法作为一种便捷的快捷方式出现,模仿了C语言的 `if()` 结构。 有趣的是,`[` 实际上是一个*命令*本身,在许多较旧的系统中,它只是 `test` 的符号链接。闭合方括号 `]` 并非由Shell强制执行,而是由 `test` 命令强制执行;当以 `[` 的形式调用时,它期望一个 `]`。 然而,现在并非总是如此。现代GNU coreutils 经常将 `test` 和 `[` 编译为独立的执行文件,尽管它们共享源代码,但使用了不同的编译标志。这可能导致细微的差异。 这种区别给一位脚本编写者带来了令人沮丧的大学经历。尽管他在可用的Linux机器上演示了 `test` 和 `[` 的等价性,但教授因为脚本在他们的SunOS系统的Bourne Shell上无法运行而对其使用 `[` 进行了惩罚,导致成绩降低。关键要点是:虽然功能相似,但 `test` 和 `[` 并不总是相同的,并且闭合方括号由命令本身验证。

黑客新闻 新 | 过去 | 评论 | 提问 | 展示 | 招聘 | 提交 登录 测试你的方括号 (fluca1978.github.io) 8 分,来自 speckx 2 小时前 | 隐藏 | 过去 | 收藏 | 讨论 指南 | 常见问题 | 列表 | API | 安全 | 法律 | 申请YC | 联系 搜索:
相关文章

原文
I do shell scripting everyday, and I’m so used to the usage of square brackets when performing some test, that I forgot the whole story. I assume you know that something like the following performs what it seems to do:
if [ -f "$file" ]; then
   echo "The file exists, avoid overwriting it!"
   exit 2
fi

The important part here is the [ ... ] test line. Let’s turn on the DeLorean circuits, and go back in time!

test, as it was and it always will be

When I was a young, green, university student, I was forced to use test(1) as the only true way to do testing in shell scripting. Therefore, the above snippet of code, was written as follows in my homework:
if test -f "$file"
then
  ...
fi

Yeah, I was also forced to not use semicolons as they were evil (according to my professor, any comment unneeded!). So, what is that test thing after all? I started exploring, and found out that test was a regular command lying somewhere in /usr/bin or /bin (or both, via a link). It’s simple enough to test test (no pun intended!) on a shell command line (i.e., outside a script):

% test -d /home/luca

% echo $?
0

So far, so good.

[ and the modern era

Then, while reading a Linux specific book, I found out that it was possible to use the [ ... ] syntax to perform something similar to what I did in C, like if ( ... ), where the parenthesis were substituted by brackets. Digging a little more, I found out that the [ was itself a command, pretty much as test. Then, doing a ls -l I discovered that test and [ were much more than similar programs, they were the same program:
% ls -l '/bin/test' '/bin/['
-r-xr-xr-x  2 root wheel 12184 Jun  6  2025 /bin/[
-r-xr-xr-x  2 root wheel 12184 Jun  6  2025 /bin/test

% sha1sum '/bin/test' '/bin/['
623e9bbe1784ebf1c8fb5d404978e7ffb7b1ef7b  /bin/test
623e9bbe1784ebf1c8fb5d404978e7ffb7b1ef7b  /bin/[

Therefore, when placing a [ into my scripts, I was silently calling test.

Understanding the closing bracket

So, after having discovered that [ was just an handy shortcut for test, where did the closing bracket come from? In the beginning I thought it was a shell syntax enforcement, but I was wrong. The turth is that test, ehm, sorry, [ knows how it has been invoked and in the case it has invoked as [ it checks for the very last argument to be a closing bracket, otherwise an error is thrown. This is clearly reported on the FreeBSD implementation of test:
if (strcmp(p, "[") == 0) {
		if (strcmp(argv[--argc], "]") != 0)
			error("missing ']'");
		argv[argc] = NULL;
	}

If the command has been invoked as [ and the last argument argv[--argc] is not ], buhm, error!

The devil is in the small details

Yesterday evening I was at a Bash course at the LUG of my city, with a friend of mine. Suddenly I remembered the whole story above, and how I was excited to prove that [ and test were the same thing. However, I found out that, on many (almost all) Linux machines these days the two commands are not anymore the same:
 ls -l '/usr/bin/test' '/usr/bin/['
-rwxr-xr-x 1 root root 55744 apr  5  2024 '/usr/bin/['
-rwxr-xr-x 1 root root 47552 apr  5  2024  /usr/bin/test

% sha1sum '/usr/bin/test' '/usr/bin/['
aba4f68d151e50aff3b4d2d6213ae6b1fd114255  /usr/bin/test
3d0fa425ddd0aca6fc4b13dcc90db07b8b53c5b0  /usr/bin/[

It turns out that GNU coreutils prefers to build the two identical programs with different compilation flags, even if the applications share the same source file and the test within it is really similar to the one that FreeBSD does.

University sad story about this

This whole thing about [ and test is really clear in my mind, even if I tend to keep it into the back of my mind memory, because it caused me a very poor evaluation of my university exam. In fact, back in those days, I thought that having found by myself that test and [ are the same application (and yes, they were linked together even in Linux, back then), I use [ ... ] in the exam script. My professor was very unhappy, and encounted a single error every time I used [ instead of test, resulting in my exam getting a final vote of 22 out of 30, where six errors where due to this substitution. I argued that, having the university laboratory a Linux based set of machines, I was testing the scripts on the only machines I had access to, but no matter, since my script was not able to run on the professor Sun OS I-don't-remember-which-version Bourne Shell, my exam did not get any chance to get a better evaluation. Simply enough to remember: test and [ are the same thing even if they do not appear to be (i.e., are different executables), and the syntax is chacked directly by the application itself, that decides if the closing bracket is required or not.
联系我们 contact @ memedata.com