Shell脚本精读 · S06-01 | `if` 结构:从单分支到 `elif` 嵌套
模块S06 控制流篇号S06-01 / 42预计阅读40 分钟主线Bash文章目录本篇目标30 秒速览正文1. if 的本质一条命令 退出码2. 最小结构if … then … fi3. else二选一4. elif多分支链5. 写法then 换行与分号6. then / else 里可以有多条命令7. 与 S05 条件表达式配合8. 嵌套 if9. if 与 || 怎么选10. 空分支与占位: 11. 读脚本时的常见模式11.1 参数检查脚本开头11.2 文件存在再操作11.3 根据环境变量分支11.4 命令成功 / 失败分支11.5 else 里接 exit12. 注意点读脚本检查清单练习判断题实操题改错题下一篇预告本篇目标掌握if…then…fi的完整语法单分支、else、elif链以及嵌套。理解if后面是一条命令、由退出码决定走哪条分支。能把 S05 的[、[[、(( ))和 S04 的grep、写进if读懂常见「先检查再执行」脚本。30 秒速览if 命令; then ... fi命令退出码为0走then否则走else/ 结束。elif前面都不满足时再试下一条条件。then前若与if同一行中间用;分隔。if后面可以是任意命令grep、[ ]、[[ ]]、(( ))、test、函数调用等。嵌套时注意fi与if配对复杂逻辑可拆成函数或提前exit。if与||简单二选一有时用连接符更短多分支用if/elif更清晰。正文1.if的本质一条命令 退出码ifgrep-qERROR$LOG;thenecho发现错误fiif后面到then之前是一条完整命令可含管道、可含。该命令成功退出码0→ 执行then块。失败非 0→ 跳过then若有else则执行else否则什么都不做。这与 S04-01 一致没有单独的「布尔类型」只有命令是否成功。iffalse;thenecho不会执行fiiftrue;thenecho会执行fi2. 最小结构if…then…fiif[-f$CONFIG];thensource$CONFIGfiif[[-d$OUT_DIR]];thenecho目录已存在fiif(($#1));thenechousage:$0file2exit1fifi结束整个if。每个if必须有匹配的fi。3.else二选一if[-f$LOCK];thenecho已有锁跳过elsetouch$LOCKrun_jobfiif[[$ENVprod]];thenset-eelsesetefi4.elif多分支链按顺序测试第一个为真的分支执行后整条if结束if[[$CMDstart]];thenstart_serviceelif[[$CMDstop]];thenstop_serviceelif[[$CMDstatus]];thenshow_statuselseechounknown command:$CMD2exit1fi等价理解if 条件1; then 块1 elif 条件2; then 块2 # 仅当 条件1 为假时才看 条件2 elif 条件3; then 块3 else 默认块 fi分支很多时下一篇S06-02的case往往更清晰elif适合少量互斥条件。5. 写法then换行与分号换行最常见if[-n$1];thenechoarg:$1fi同一行时then前必须有;if[-n$1];thenechoarg:$1;fielse/elif后的then同样规则if[-f$a];thenuse_aelif[-f$b];thenuse_belseuse_defaultfi6.then/else里可以有多条命令if[-d$DIR];thenecho进入$DIRcd$DIR||exit1lselseecho创建$DIRmkdir-p$DIRfi块内最后一条命令的退出码会成为「整个块」的退出码在子 shell 外执行时if本身的成功与否仍由if后面那条测试命令决定不是由块内最后一条决定。7. 与 S05 条件表达式配合if后常见写法来源[ -f $f ]S05-01、S05-02[[ $x *.log ]]S05-03(( $# 1 ))S05-04grep -q pattern file普通命令if[[-f$CONFIG-r$CONFIG]];thensource$CONFIGfiif((retrymax_retry));then((retry))sleep1fi组合命令一条if条件里多个检查if[-n$1][-f$1];thenprocess$1fiif[[-f$LOG]]grep-qERROR$LOG;thenalertfi8. 嵌套ifif[-f$CONFIG];thenifgrep-q^DEBUG1$CONFIG;thenexportDEBUG1fielseechomissing config2exit1fifi配对内层if先闭合再外层if 外; then if 内; then ... fi ← 内层 fi else ... fi ← 外层 fi嵌套过深时可改为elif、提前 return/exit或函数# 扁平化示例if[!-f$CONFIG];thenechomissing config2exit1fiifgrep-q^DEBUG1$CONFIG;thenexportDEBUG1fi9.if与||怎么选场景常见写法失败则退出cmd || exit 1成功才继续cmd next二选一、无 else 块cmd1 || cmd2多分支、带多块语句if/elif/else# 短仅当目录存在才 cd[-d$DIR]cd$DIR# 长存在则 cd否则创建再 cdif[-d$DIR];thencd$DIRelsemkdir-p$DIRcd$DIRfi可读性超过两行逻辑或超过两个分支优先if。10. 空分支与占位:then或else不能为空需要占位时用:空操作恒成功if[-f$SKIP];then:elserun_taskfi11. 读脚本时的常见模式11.1 参数检查脚本开头if(($#1));thenechousage:$0srcdir2exit1fisrcdir$111.2 文件存在再操作if[[!-f$INPUT]];thenechonot found:$INPUT2exit1fi11.3 根据环境变量分支if[[${VERBOSE:-}1]];thenset-xfi11.4 命令成功 / 失败分支ifmake-j$(nproc);thenechobuild okelseechobuild failed2exit1fi11.5else里接exitif[[-x$SCRIPT]];then$SCRIPTelseechonot executable:$SCRIPT2exit1fi12. 注意点点说明if测的是一条命令不要写成没有命令的「假 C 语法」[、[[是命令需要空格、引号S05管道在if后if cmd1 | cmd2; then整条管道的退出码规则见 S09set -ethen里某条失败可能直接退出脚本S01-04缩进不强制但then/else/fi对齐便于核对配对读脚本检查清单if与fi是否成对、嵌套层次是否清楚elif顺序是否合理更具体的条件是否放在前面条件里的变量、路径是否加了$var应用(( ))的地方是否误写在[[ ]]里用简单「有则执行」是否本可用却写了冗长if或相反练习判断题if后面可以写grep -q pattern file。elif块只有在前面所有条件都为假时才会判断。if [ -f $f ]; then在$f含空格时仍安全。if false; then echo a; else echo b; fi会输出b。同一行写if true then echo ok fi合法。参考答案对。对。错应$f。对。错then前需;if true; then ...。实操题补全脚本第一个参数为start、stop、restart之一时打印对应动作否则打印 usage 并以退出码 2 结束。#!/usr/bin/env bashcmd${1:-}# 在此补全 if / elif / else参考答案if[[$cmdstart]];thenechostartingelif[[$cmdstop]];thenechostoppingelif[[$cmdrestart]];thenechorestartingelseechousage:$0start|stop|restart2exit2fi改错题if[-f$1]thencat$1elif[-d$1]echois direlseechomissingfi参考if[-f$1];thencat--$1elif[-d$1];thenechois direlseechomissingfielif后缺then变量需引号。下一篇预告S06-02《case模式匹配多分支可读写法》— 用case $var in ... esac替代长elif链并与 S05-03 的模式规则呼应。