Shell 脚本里的冒号:无用却必用

A shell colon does nothing. Use it anyway

Shell 脚本里的冒号:无用却必用

我写过无数 Shell 脚本,却总被一些冷门技巧惊得目瞪口呆。最近让我大开眼界的,就是那个看似毫无作用的冒号。它不仅是参数扩展中检查必填参数的利器,能一键替代冗长的 if 判断;作为 null command,它还能在设置默认值、截断文件或检查文件权限时发挥奇效。这个源自 1971 年 Thompson shell 的古老符号,如今依然是 Unix 哲学中优雅简洁的典范。

Shell 里的冒号看似什么都不做,但你依然应该使用它。
  1. kazinator

    > ( : < dataset.json ) && echo YES # dataset.json 可读吗?

    这里的子 shell 执行括号和冒号命令完全是多余的,直接写:

    < dataset.json && echo YES

    重定向不需要挂一个冒号命令,也没必要 fork 一个子 shell 来执行这种命令。

    > ( : >> result.json ) && echo YES # result.json 可写吗?

    作为一个检查可写性的惯用写法,这让我有点犹豫。如果文件不存在,我们就会创建一个零长度的文件。如果接下来的操作反正就是要往里面写东西,那可能还行。

    如果我们是为了覆盖它而做测试,为什么不直接用 "> result.json"(这本身就是一个将文件截断为零长度的惯用写法)呢?

    我们什么时候会用到这种写法?也许是在某个命令之前,该命令把文件名作为目标文件参数传入(而不是使用输出重定向),并且在尝试打开文件写入之前会进行耗时的计算。这样我们可以尽早捕获权限错误。

    我想我从来没写过这种测试;通常的做法是直接执行写入操作,让它失败就是了。

    在 POSIX C 中,有一个 access() 函数用于执行这类测试。但它有特殊用途:它是给 setuid root 进程用的,用来以真实用户/组(即提升权限到 root 的那个用户/组)的身份执行权限检查。也就是说,它检查的不是“我们能不能做这个操作”,而是“我们应该做这个操作吗”(如果我们把权限降回原始用户,是否仍然被允许)。

  2. zaptheimpaler

    人生苦短,没必要为了超过两行的脚本去忍受这门语言及其五万个“地雷”,尤其是在大模型时代。直接写 Python、TS 或任何真正的编程语言脚本吧。Bash 在命令行上很棒,但也应该仅限于此。

  3. kevincox

    我对其中大多数用法都不太感冒,但有几个确实挺有用。

    : "${1:?missing argument, aborting!}"

    我一般不会这么用,因为我会想把 $1 赋值给一个变量,以便在脚本后续部分使用。但这确实是一个给出清晰错误信息的好方法,用于处理缺失的必需环境变量。

    其他很多用法(比如截断文件)可能用专用命令写得更清楚,但如果你极力避免依赖 shell 之外的东西,它们可能会派上用场。

同日更多故事

2026-07-26