每天都在和 Linux 打交道,那么 Shell 基本就是最高频的工具之一。无论是处理文件、安装软件,还是写一些顺手的小脚本,很多操作都离不开命令行。而在日常使用中,一个很常见的需求就是把多条命令串起来执行。这样可以少敲几次回车,也能把连续操作写得更紧凑一些。
Shell 里最常见的三种命令连接方式是 ;、&& 和 ||。它们看起来只是几个简单符号,但执行逻辑并不一样。真正理解这三者的区别,能少踩很多坑。
;:前后命令都会执行
语法很简单:
cmd1 ; cmd2
它的含义是:不管 cmd1 成功还是失败,cmd2 都会继续执行。
例如:
whoami; cat zzh0u.md; date
这条命令会按顺序做三件事:
- 输出当前登录用户
- 尝试读取
zzh0u.md - 输出当前时间
即使 cat zzh0u.md 失败了,最后的 date 还是会继续执行。
所以,; 更适合那些彼此独立的命令。比如前一条命令失败了,也不影响后一条命令继续跑。不过它也是最容易埋坑的一种写法。因为 Shell 不会帮你判断前一步是不是成功了。比如下面这条命令:
cd .. ; rm -rf *
如果 cd .. 写错了(oh-my-zsh 或某些终端主题支持用 cd ... 替代 cd ../..),或者目录根本不存在,后面的 rm -rf * 仍然会执行。这种情况下,删掉的就未必是你想删的东西了。
&&:前一条成功,后一条才执行
语法如下:
cmd1 && cmd2
&& 可以理解为逻辑与。只有当 cmd1 执行成功,cmd2 才会继续执行。例如:
mkdir demo && cd demo && touch main.go
这条命令的执行逻辑是:
- 先创建
demo目录 - 创建成功后再进入该目录
- 成功进入目录后再创建
main.go
只要中间任意一步失败,后续命令都会停止。
这也是我更推荐日常优先使用的连接方式,尤其是在“后一条命令依赖前一条命令结果”的场景里。相比 ;,&& 的安全性会高很多。还是上面的例子,如果改成:
cd .. && rm -rf *
那么只有在成功切换到目标目录之后,删除命令才会执行。这个习惯在处理文件、跑部署脚本、批量改目录时都很重要。
||:前一条失败,后一条才执行
语法如下:
cmd1 || cmd2
|| 可以理解为逻辑或。只有当 cmd1 执行失败时,cmd2 才会执行。它常用于“失败后的兜底逻辑”。
例如:
find zzh0u.md || touch zzh0u.md
这条命令的含义是:
- 如果
zzh0u.md已经存在,find返回成功,touch就不会执行 - 如果
zzh0u.md不存在,find返回失败,后面的touch就会创建这个文件
这里顺手提一句,
touch在文件已存在时通常也会执行成功,它更多是更新时间戳,而不是仅在文件不存在时创建。
|| 的另一个常见用途是提供降级方案,例如:
command -v brew >/dev/null 2>&1 || echo "brew is not installed"
如果系统里没有 brew,就输出一条提示信息。
什么时候该用哪一个?
可以简单记成下面三条:
- 不关心前一条命令是否成功,只想按顺序都执行:用
; - 后一条命令依赖前一条命令成功:用
&& - 希望在前一条命令失败时执行兜底逻辑:用
||
如果只是图省事把所有命令都用 ; 串起来,短期看起来方便,长期往往更容易出问题。尤其是涉及 cd、rm、mv 这类命令时,&& 往往是更稳妥的选择。
;、&& 和 || 都能把多条命令写在一行里,但它们控制的是完全不同的执行条件。很多时候,Shell 命令写得稳不稳,不在于会不会敲命令,而在于有没有把执行条件写清楚。