Linux shell 编程进阶技巧

2次阅读

set -e 仅对单独命令或管道末尾命令生效,不捕获管道中间失败、if/&&/|| 中非末尾命令;需显式判断 $? 或用 ${var:?} 校验变量;循环应避免 for f in $(ls),改用 find -print0 | while read -r -d ”;set -x 需配对 set +x 防止日志泄露。

Linux shell 编程进阶技巧

set -e 之前先搞清它到底在什么情况下不退出

很多人加了 set -e 就以为脚本“出错自动停”,结果发现 grep 没匹配到却没退出,|| 后面的命令照常执行——因为 set -e 对管道、if 判断、&&/|| 链里的非最后一个命令默认不生效。

真正可靠的写法是:把关键检查单独成行,或显式判断返回值:

grep "pattern" file.txt if [ $? -ne 0 ]; then exit 1; fi

常见踩坑点:

  • set -e 不捕获 command || echo "fallback"command 的失败
  • 管道中只有最后一个命令失败才会触发退出:cat file.txt | grep "x" | sort,中间 grep 失败不会退出
  • 函数内部的 set -e 不会继承到调用者作用域

变量未定义就引用?${var:?}test -z "$var" 更直接

空值或未定义变量导致脚本逻辑错乱,但每次手动 if [ -z "$var" ] 太啰嗦。直接用参数扩展强制校验更省事,也更早暴露问题。

实操建议:

  • 必须存在的变量:用 ${var:? "var is required"},未定义或为空时立即报错退出
  • 允许为空但不能未定义:用 ${var?}(注意没冒号),只对未定义报错
  • 别混用 ${var:-default}${var:?}——前者是兜底,后者是断言,目的完全不同
  • source 配置文件里大量用 ${var:?},比运行时才发现变量缺失强得多

循环处理文件名含空格?别用 for f in `ls`

for f in `ls`for f in $(find ...) 在遇到带空格、换行、* 等特殊字符的文件名时必然崩,这不是边缘情况,而是日常。

可靠方案只有两个:

  • while read -r 配合 find -print0
    find . -name "*.log" -print0 | while IFS= read -r -d '' file; do   echo "Processing: $file" done
  • 用数组 + globbash 4.4+):
    files=( *.log ) for f in "${files[@]}"; do   [ -e "$f" ] || continue  # 防空匹配   echo "Processing: $f" done
  • 永远别在循环变量里直接插 $f,一律用 "$f" 包裹

调试时想看每条命令实际执行啥?set -x 要和 set +x 配对关

set -x 打印执行前的命令(带变量展开),非常有用,但开太久会导致日志爆炸,尤其在循环或递归调用里。

实用习惯:

  • 只在怀疑段落前后加:
    set -x some_tricky_command arg1 "$var" set +x
  • set -x 输出里的 + 是提示符,不是命令一部分;看到 + cd /tmp 说明 cd 真被执行了
  • 如果脚本被其他脚本 sourceset -x 会影响父脚本,所以优先用局部子 shell:
    (set -x; some_command)
  • 别依赖 set -x 查变量值——它只显示展开后的命令,不显示变量原始内容

最常被忽略的是:set -x 开启后,所有后续子 shell、函数调用都会继承,除非显式关闭。漏关 set +x 的脚本上线后可能把敏感路径、参数全打到日志里。

text=ZqhQzanResources