Shell 脚本系统学习 · 第5篇:多命令顺序执行的三种方式详解(`;`、``、`||`)

在日常的 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. 小结

  • 作用:仅在前一命令失败时才执行下一命令。
  • 适合场景:针对“异常/失败”进行补救、日志记录、报警通知等。

五、三者结合使用示例

在实际脚本中,常将 ;&&|| 混合使用,实现更灵活的流程控制。下面示例演示:

  1. 部署 Tomcat 9.0.105
  2. 下载地址:https://archive.apache.org/dist/tomcat/tomcat-9/v9.0.100/bin/apache-tomcat-9.0.100.tar.gz
  3. 工作目录:/data/web-service
  4. 日志路径:/data/web-service/logs/catalina.out
  5. 当然如果你想下载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 成功与否;失败则用大括号 { … } 打印日志并退出。

六、常见误区与注意事项

  1. 不加大括号导致逻辑混淆

    cmd1 && cmd2 || cmd3
    
    • 含义:如果 cmd1 成功,才会执行 cmd2;但无论 cmd1cmd2 哪一个失败,都会执行 cmd3

    • 若想实现“仅当 cmd1 成功且 cmd2 失败时才 cmd3”,需加大括号:

      cmd1 && { cmd2 || cmd3; }
      
  2. 分号与换行的等价性

    cmd1; cmd2
    

    等同于:

    cmd1
    cmd2
    

    仅是写法不同。分号适合一行写多个短命令;若逻辑复杂,推荐换行并加注释。

  3. 混合使用时要注意优先级

    • Shell 会先解析 &&||,最后处理 ;
    • 当同时出现 &&||; 时,建议适当用“换行 + 大括号”来让逻辑更清晰,避免歧义。

七、小白友好的命令对照表

操作符含义示例适用场景
;无条件顺序执行echo A ; echo B ; echo C不关心命令是否成功,只要全部都执行一次
&&成功则继续执行mkdir /tmp/dir && echo "创建成功"仅在前一步成功时才做后续操作
||失败则继续执行cp A B || echo “复制失败”用于失败补救、日志记录、报警通知

八、运维示例脚本:一键日志轮转与备份

下面附上一个可直接拿来使用的运维脚本,通过 ;&&|| 三种方式,实现对指定服务日志的轮转、压缩、备份,并保留最近 7 天的历史。适用于 CentOS/Ubuntu 等常见 Linux 发行版。

脚本功能概览

  1. 停止目标服务
  2. 将日志文件按日期重命名并压缩备份到指定目录
  3. 删除 7 天前的旧备份
  4. 启动目标服务
  5. 将执行结果写入操作日
    脚本下载方式,关注公众号【安全日记Pro】,进入发送消息,点击shell菜单,获取脚本

📝 脚本使用说明

  1. 保存脚本
    将以上内容保存为 log_rotate_backup.sh,并赋予可执行权限:

    chmod +x log_rotate_backup.sh
    
  2. 执行脚本示例

    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

  1. 定时执行(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"
    

九、结语与小白学习建议

  1. 多练、多试

    • 亲手在终端尝试不同的 ;&&|| 组合,观察 $? 的返回值和后续命令的执行情况,体验它们的执行逻辑。
  2. 使用 set -e(可选)

    • 在脚本开头添加 set -e,可以让脚本在任意一步出现非 0 返回值时立即退出。但要注意与 ||&& 的混用可能导致流程不如预期,初学者可先不加,待熟悉后再灵活运用。
  3. 日志与错误处理很关键

    • 像示例脚本一样,将执行结果写到日志文件,并在失败时发送报警或退出脚本,是做好运维脚本的关键。
  4. 牢记返回值判断

    • 在 Shell 中,返回值 0 = 成功,非 0 = 失败。掌握这一点,就能自然而然地运用 &&|| 进行流程控制。

本篇小结

  • ;:无条件顺序执行,所有命令都会尝试执行。
  • &&:前一步成功时才执行下一步,适合级联式操作。
  • ||:前一步失败时才执行下一步,适合“失败补救”或“日志/报警”。
  • 混合使用时要加大括号或换行,避免逻辑歧义。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/91301.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/91301.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

K8s存储系统(通俗易懂版)

Kubernetes中存储中有四个重要的概念:Volume、PersistentVolume PV、PersistentVolumeClaim PVC、StorageClass一、存储系统核心概念Volume(卷)定义:Kubernetes 中最基础的存储单元,用于将外部存储挂载到 Pod 中的容器…

小白学Python,标准库篇——随机库、正则表达式库

一、随机库1.随机生成数值在random库中可以随机生成数值的方法有uniform()、random()、randint()、randrange()等。(1)uniform()方法uniform(参数1, 参数2)方法用于生成参数1到参数2之间的随机小数,其中参数的类型都为数值类型。示例代码&…

Qt窗口:菜单栏

目录 一、窗口预览 二、菜单栏 快捷键 子菜单 分割线 图标 内存泄露 一、窗口预览 在前面几篇文章中,或者说,Qt初学阶段,接触到的都是QWidget,QWidget指控件,往往作为一个窗口的一部分出现。所谓的窗口&#x…

STM32裸机开发(中断,轮询,状态机)与freeRTOS

裸机:没有操作系统,程序是单流程的(比如一个大循环里依次执行各个功能,或者用中断嵌套处理事件)。优点是资源占用极少(几乎不占 RAM/Flash)、执行流程直观;但复杂项目里,…

电脑上如何查看WiFi密码

打开控制面板>点击网络和Internet在查看网络和共享中心找到网络状态和任务点击进去点击连接的WLAN在WLAN状态中点击无线属性在无线网络属性中点击安全,点击显示字符(H)就可以显示密码了

文心一言4.5企业级部署实战:多模态能力与Docker容器化测评

随着大语言模型在企业服务中的应用日益广泛,如何选择一款既能满足多模态创作需求,又具备良好企业级适配性的AI模型成为了关键问题。文心一言4.5作为百度最新开源的大模型,不仅在传统的文本处理上表现出色,更是在多模态理解和企业级…

VUE Promise基础语法

目录 异步和同步 异步的问题 new Promise语法 promise的状态 promise.then() Promise.resolve() Promise.reject() Promise.all() Promise.race() Promise.catch() Promise.finally() 异步和同步 同步模式下,代码按顺序执行,前一条执行完毕后…

用TensorFlow进行逻辑回归(六)

import tensorflow as tfimport numpy as npfrom tensorflow.keras.datasets import mnistimport time# MNIST数据集参数num_classes 10 # 数字0到9, 10类num_features 784 # 28*28# 训练参数learning_rate 0.01training_steps 1000batch_size 256display_step 50# 预处…

【HTTP版本演变】

在浏览器中输入URL并按回车之后会发生什么1. 输入URL并解析输入URL后,浏览器会解析出协议、主机、端口、路径等信息,并构造一个HTTP请求(浏览器会根据请求头判断是否又HTTP缓存,并根据是否有缓存决定从服务器获取资源还是使用缓存…

Android 16系统源码_窗口动画(一)窗口过渡动画层级图分析

一 窗口过渡动画 1.1 案例效果图1.2 案例源码 1.2.1 添加权限 (AndroidManifest.xml) <!-- 系统悬浮窗权限&#xff08;Android 6.0需动态请求&#xff09; --> <uses-permission android:name"android.permission.SYSTEM_ALERT_WINDOW" />1.2.2 窗口显示…

腾讯云WAF域名分级防护实战笔记

基于业务风险等级、合规要求及腾讯云最佳实践&#xff0c;提供可直接落地的配置方案&#xff0c;供学习借鉴&#xff1a;一、域名分级与防护原则1. ​域名分级清单&#xff08;核心资产&#xff09;​​​主域名​​业务类型​​风险等级​​合规要求​​防护等级​example.com…

1. 请说出你知道的水平垂直居中的方法

总结 容器 flex 布局&#xff0c;jsutify-content: center; align-items: center;容器 flex 布局&#xff0c;子项 margin: auto;容器 relative 布局&#xff0c;子项 absolute 布局&#xff0c;left: 50%; top: 50%; transform: translate(-50%, -50%);子项 absolute 布局&…

VS Code `launch.json` 完整配置指南:参数详解 + 配置实例

文章目录&#x1f4e6; 一、基本结构&#x1f50d; 二、单个配置项详解示例配置&#xff1a;&#x1f9e9; 三、字段说明与可选值&#x1f4c1; 四、常用变量&#xff08;宏替换&#xff09;&#x1f6e0;️ 五、常见配置实例1️⃣ 调试当前打开的 .py 文件2️⃣ 调试 Jupyter …

使用浏览器inspect调试wx小程序

edge://inspect/#devices调试wx小程序 背景&#xff1a; 在开发混合项目的过程中&#xff0c;常常需要在app环境排查问题&#xff0c;接口可以使用fiddler等工具来抓包&#xff0c;但是js错误就不好抓包了&#xff0c;这里介绍一种调试工具-浏览器。 调试过程 首先电脑打开edg…

【论文阅读】-《Simple Black-box Adversarial Attacks》

简单黑盒对抗攻击 Chuan Guo Jacob R. Gardner Yurong You Andrew Gordon Wilson Kilian Q. Weinberger 摘要 我们提出了一种在黑盒&#xff08;black-box&#xff09;场景下构建对抗样本&#xff08;adversarial images&#xff09;的极其简单的方法。与白盒&#xff08;…

基于ASP.NET+SQL Server实现(Web)企业进销存管理系统

企业进销存管理系统的设计和实现一、摘要进销存管理是现代企业生产经营中的重要环节&#xff0c;是完成企业资源配置的重要管理工作&#xff0c;对企业生产经营效率的最大化发挥着重要作用。本文以我国中小企业的进销存管理为研究对象&#xff0c;描述了企业进销存管理系统从需…

(LeetCode 面试经典 150 题 ) 15. 三数之和 (排序+双指针)

题目&#xff1a;15. 三数之和 思路&#xff1a;排序双指针&#xff0c;时间复杂度0(n^2nlogn)。 先将数组nums升序排序&#xff0c;方便去重和使用双指针。第一层for循环来枚举第一位数&#xff0c;后面使用双指针来找到第二个、第三个数即可&#xff0c;细节看注释。 C版本…

easy-springdoc

介绍 简化springdoc的使用&#xff08;可以搭配knife4j-openapi3-jakarta-spring-boot-starter一起使用&#xff09; maven引用 <dependency><groupId>io.github.xiaoyudeguang</groupId><artifactId>easy-springdoc</artifactId><version>…

配置nodejs,若依

1.配置node.js环境 Node.js — Download Node.js 1.下载好一路下一步&#xff0c;可以安装到d盘 装完之后执行 npm -v 显示版本号即安装成功 2.安装好后新建两个文件夹&#xff0c;node_cache和node_global 3.配置环境变量 新建变量 在path里编辑变量 4.配置用户变量 5.…

Python学习之路(十二)-开发和优化处理大数据量接口

文章目录一、接口设计原则二、性能优化策略1. 数据库优化2. 缓存机制3. 并发模型三、内存管理技巧1. 内存优化实践2. 避免内存泄漏四、接口测试与监控1. 性能测试2. 日志与监控3. 错误处理与限流五、代码示例&#xff08;Flask 流式处理&#xff09;六、部署建议一、接口设计原…