在日常的 Linux 运维与脚本编写中,我们经常需要依次执行多条命令。本篇将带你彻底搞懂三种命令顺序执行方式:;
、&&
和 ||
,并通过实用示例掌握它们的区别与应用场景。
一、为什么要了解多命令执行方式?
在实际运维或脚本编写中,往往需要连续执行多条命令:
- 有时希望某条命令出错也能继续往下做;
- 有时希望后续命令仅在前面执行成功后才运行;
- 也有时要在某条命令失败时额外做补救。
掌握 ;
、&&
、||
这三种操作符,就能灵活控制“先后顺序”与“成功/失败依赖”,让脚本更健壮、更易维护。
二、;
— 命令分隔符也叫顺序执行符
表示:两个命令是独立的,无论前一个命令执行结果如何,后一个都会执行
1. 语法格式
命令1 ; 命令2 ; 命令3 ; …
解释:使用分号(;
)将多条命令分隔,Shell 会依次执行它们,无论前一条命令是否成功,都会继续执行下一条。
2. 示例
- 命令1,whoami 查看当前用户
- 命令2,cat file.txt 查看当前目录file.txt文件(这里file.txt不存在,执行时会报错,也就是第二条命令执行失败)
- 命令3,ls -la 查看当前目录的所有文件/目录
#!/bin/bash
whoami;cat file.txt;ls -la
3. 输出结果
即使第二条写成了 cet file.txt
(写错命令),也不会阻止第三条执行(下面我把 cat
改为 cet
)
#!/bin/bash
whoami;cet file.txt;ls -la
4. 详细说明
- 执行顺序:从左到右逐条执行。
- 错误忽略:前一条返回非 0 (失败)也不影响下一条的执行。
- 适用场景:当你只需保证“一行里列出的命令都会尝试执行一次”,不在乎它有没有出错。例如:批量创建目录后,按顺序设置权限、启动服务等,即使中间某步出错,也要继续尝试后面操作。
5. 小结
- 作用:无条件顺序执行,多条命令都会执行。
- 适合场景:不关心某步是否成功,只要“都试一遍”即可。
- windows CMD中,
;
对应 Windows 的&
其他两个都一样
三、&&
—— 逻辑与运算符
表示:如果前面的命令执行成功,再执行后面的命令
1. 语法格式
命令1 && 命令2 && 命令3 && …
解释:只有当 命令1
返回值为 0(成功)时,才会执行 命令2
;同理,命令2
成功后才执行 命令3
。一旦某条命令失败(非 0),后续命令将不再执行。
2. 示例
当前目录下不存在test
目录,所以执行 cd test
时会提示错误
#!/bin/bash
echo "执行第一条命令" && cd test && "执行第三条命令"
cd test && echo "执行第二条命令" && "执行第三条命令"
3. 输出示例
4. 详细说明
- 成功触发:
&&
链中的一项只有在前一项成功时才执行。 - 返回值:整个链式命令的返回值等于最后一个成功执行的命令返回值;若中途失败,则返回失败的那条命令的返回值。
- 适用场景:
- 多步骤“级联”操作:如先下载包、再解压、再安装,任何一步失败都要停止后续。
- 条件判断:如先检查网络连通性,再执行更新;若检查失败,后续操作不执行。
5. 小结
- 作用:仅在前一命令成功时才执行下一命令。
- 适合场景:多步骤“必须成功才能往下做”的情况。
四、||
—— 逻辑或运算
前命令执行失败了,才执行后面的命令
1. 语法格式
命令1 || 命令2 || 命令3 || …
解释:只有当 命令1
返回非 0(失败)时,才会执行 命令2
;若 命令1
成功(返回 0),则跳过 命令2
。依次类推:前一条失败时才会继续执行下一条。
2. 示例:
当前目录下不存在 data 目录
#!/bin/bash
cd data || pwd
pwd || cd /data
3. 输出结果
- cd data 进入当前目录下的 data 目录
- 如果 data 目录不存在(判断返回非 0),才执行
pwd
;
Tip:为了逻辑更清晰,shell脚本中通常会写成
if … else
结构,避免单行逻辑混淆。后续我会出条件判断文章,一步一步来哦~
4. 小结
- 作用:仅在前一命令失败时才执行下一命令。
- 适合场景:针对“异常/失败”进行补救、日志记录、报警通知等。
五、三者结合使用示例
在实际脚本中,常将 ;
、&&
、||
混合使用,实现更灵活的流程控制。下面示例演示:
- 部署
Tomcat 9.0.105
; - 下载地址:
https://archive.apache.org/dist/tomcat/tomcat-9/v9.0.100/bin/apache-tomcat-9.0.100.tar.gz
- 工作目录:
/data/web-service
- 日志路径:
/data/web-service/logs/catalina.out
- 当然如果你想下载
tomcat 8、10、11
只需要替换DOWNLOAD_URL
变量中的数字即可
#!/bin/bash
# 定义变量
WORK_DIR="/data/web-service" #定义工作目录变量
TOMCAT_VERSION="9.0.100" #定义tomcat小版本变量
TAR_NAME="apache-tomcat-${TOMCAT_VERSION}.tar.gz" #定义tomcat下载包变量
DOWNLOAD_URL="https://archive.apache.org/dist/tomcat/tomcat-9/v${TOMCAT_VERSION}/bin/${TAR_NAME}" #定义tomcat下载路径,以及大版本,如果想下载tomcat8,直接把这里9改成8,然后修改对应的小版本号LOG_DIR="${WORK_DIR}/logs" #定义日志目录
LOG_FILE="${LOG_DIR}/catalina.out"# 创建工作目录和日志目录
mkdir -p "$WORK_DIR" "$LOG_DIR" && echo "【创建目录】工作目录和日志目录已创建" || { echo "【错误】无法创建目录"; exit 1; }
cd "$WORK_DIR" || { echo "【错误】无法进入 $WORK_DIR"; exit 1; }
echo "【开始部署】Tomcat ${TOMCAT_VERSION} 部署开始于 $(date '+%F %T')"# 清空旧日志文件
> "$LOG_FILE" && echo "【初始化】日志文件已清空" || echo "【警告】日志文件不存在或无法清空"# 检查是否已有 Tomcat 目录,有则删除
[ -d "apache-tomcat-${TOMCAT_VERSION}" ] && rm -rf "apache-tomcat-${TOMCAT_VERSION}" && echo "【清理】旧版 Tomcat 已删除" ; echo "【检查】当前无旧版 Tomcat 或已清理完毕"# 下载 Tomcat(如果不存在)
if [ ! -f "$TAR_NAME" ]; thenecho "【下载】开始下载 Tomcat..."wget -q --show-progress "$DOWNLOAD_URL"if [ $? -eq 0 ]; thenecho "【下载】Tomcat 已下载成功"elseecho "【错误】下载失败,请检查网络或 URL"exit 1fi
elseecho "【下载】安装包已存在,跳过下载..."
fi# 解压 Tomcat
tar -zxpf "$TAR_NAME" > /dev/null 2>&1 && echo "【解压】Tomcat 解压成功" || { echo "【错误】解压失败,请检查压缩包"; exit 1; }# 启动 Tomcat(后台运行),输出重定向到日志文件
./apache-tomcat-${TOMCAT_VERSION}/bin/startup.sh >> "$LOG_FILE" 2>&1 &
if [ $? -eq 0 ]; thenecho "【启动】Tomcat 已启动(后台运行)"
elseecho "【错误】启动失败,请查看日志文件"exit 1
fi# 等待几秒确保进程已经启动
sleep 3# 输出 Tomcat 进程信息
PID=$(ps -ef | grep java | grep "apache-tomcat-${TOMCAT_VERSION}" | awk '{print $2}')
if [ -n "$PID" ]; thenecho "【状态】Tomcat 已运行,PID: $PID"
elseecho "【警告】未找到 Tomcat 进程,请检查日志"
fiecho "【完成】部署流程结束于 $(date '+%F %T')"
要点解析:
- 第1步 用
||
判断目录是否存在,否则创建;再用&&
打印日志。- 第2步 用
&&
判断cd
成功与否;失败则用大括号{ … }
打印日志并退出。
六、常见误区与注意事项
-
不加大括号导致逻辑混淆
cmd1 && cmd2 || cmd3
-
含义:如果
cmd1
成功,才会执行cmd2
;但无论cmd1
或cmd2
哪一个失败,都会执行cmd3
。 -
若想实现“仅当
cmd1
成功且cmd2
失败时才cmd3
”,需加大括号:cmd1 && { cmd2 || cmd3; }
-
-
分号与换行的等价性
cmd1; cmd2
等同于:
cmd1 cmd2
仅是写法不同。分号适合一行写多个短命令;若逻辑复杂,推荐换行并加注释。
-
混合使用时要注意优先级
- Shell 会先解析
&&
、||
,最后处理;
。 - 当同时出现
&&
、||
、;
时,建议适当用“换行 + 大括号”来让逻辑更清晰,避免歧义。
- Shell 会先解析
七、小白友好的命令对照表
操作符 | 含义 | 示例 | 适用场景 |
---|---|---|---|
; | 无条件顺序执行 | echo A ; echo B ; echo C | 不关心命令是否成功,只要全部都执行一次 |
&& | 成功则继续执行 | mkdir /tmp/dir && echo "创建成功" | 仅在前一步成功时才做后续操作 |
|| | 失败则继续执行 | cp A B || echo “复制失败” | 用于失败补救、日志记录、报警通知 |
八、运维示例脚本:一键日志轮转与备份
下面附上一个可直接拿来使用的运维脚本,通过 ;
、&&
、||
三种方式,实现对指定服务日志的轮转、压缩、备份,并保留最近 7 天的历史。适用于 CentOS/Ubuntu 等常见 Linux 发行版。
脚本功能概览
- 停止目标服务
- 将日志文件按日期重命名并压缩备份到指定目录
- 删除 7 天前的旧备份
- 启动目标服务
- 将执行结果写入操作日
脚本下载方式,关注公众号【安全日记Pro】,进入发送消息,点击shell菜单,获取脚本
📝 脚本使用说明
-
保存脚本
将以上内容保存为log_rotate_backup.sh
,并赋予可执行权限:chmod +x log_rotate_backup.sh
-
执行脚本示例
sudo sh log_rotate_backup.sh \tomcat "/var/log/tomcat/access.log" \"/backup/logs/tomcat" "/var/log/tomcat/rotate.log"
- 脚本会对
/var/log/tomcat/access.log
进行轮转备份, - 备份文件存放在
/backup/logs/tomcat/2025-06/
目录下, - 并将操作记录写入
/var/log/tomcat/rotate.log
。
- 脚本会对
-
定时执行(Crontab 可选)
可将脚本加入 crontab,实现每日自动轮转。例如每天凌晨 1 点执行:#脚本路径根据你实际情况填写 0 1 * * * /usr/local/bin/log_rotate_backup.sh \tomcat "/var/log/tomcat/access.log" \"/backup/logs/tomcat" "/var/log/tomcat/rotate.log"
九、结语与小白学习建议
-
多练、多试
- 亲手在终端尝试不同的
;
、&&
、||
组合,观察$?
的返回值和后续命令的执行情况,体验它们的执行逻辑。
- 亲手在终端尝试不同的
-
使用
set -e
(可选)- 在脚本开头添加
set -e
,可以让脚本在任意一步出现非 0 返回值时立即退出。但要注意与||
、&&
的混用可能导致流程不如预期,初学者可先不加,待熟悉后再灵活运用。
- 在脚本开头添加
-
日志与错误处理很关键
- 像示例脚本一样,将执行结果写到日志文件,并在失败时发送报警或退出脚本,是做好运维脚本的关键。
-
牢记返回值判断
- 在 Shell 中,返回值 0 = 成功,非 0 = 失败。掌握这一点,就能自然而然地运用
&&
、||
进行流程控制。
- 在 Shell 中,返回值 0 = 成功,非 0 = 失败。掌握这一点,就能自然而然地运用
本篇小结
;
:无条件顺序执行,所有命令都会尝试执行。&&
:前一步成功时才执行下一步,适合级联式操作。||
:前一步失败时才执行下一步,适合“失败补救”或“日志/报警”。- 混合使用时要加大括号或换行,避免逻辑歧义。