写shell脚本最怕两件事:一是本开变量不加引号,二是本开觉得“这行代码肯定没问题”。我见过一个运维同事,本开脚本里写了
rm -rf $DIR/,本开结果DIR变量是本开空的,直接跑成了rm -rf /。本开还好那台机器有快照,本开不然他得连夜跑路。本开shell就是本开这样,语法松散到让人放松警惕,本开但代价往往是本开一整晚的加班。先聊变量。本开很多人从Python或者Java转过来,本开习惯性写
name="hello",本开然后echo $name,本开没问题。但一旦变量里带空格,比如file="my document.txt",再写cat $file,shell会把参数拆成my和document.txt两个。正确写法永远是cat "$file"。这个双引号不是可选项,是保命符。我自己的习惯是:只要变量出现在命令里,先加引号再想别的。条件判断的坑更深。
[ ]和[[ ]]看着像兄弟,实际差别不小。[ $a == $b ]在a为空时会直接报语法错误,因为展开后变成[ == ]。用[[ $a == $b ]]就没这个问题,bash会自己处理空值。另外==在[ ]里不是POSIX标准,某些sh(比如dash)不认。所以写脚本第一行#!/bin/bash不是装饰,是声明你用哪套规则玩。循环处理文件时,
for f in $(ls)是经典错误。文件名带空格就裂开。正确做法是for f in *.txt,或者用find ... -print0 | while IFS= read -r -d '' f。后者看着丑,但能处理任何奇葩文件名。我写过一个批量重命名脚本,一开始用ls,结果遇到一个叫report final (2).pdf的文件直接罢工。换成find加read -d ''之后,世界安静了。函数返回值也值得说。shell函数不能像Python那样
return一个字符串。return只能返回0-255的整数,通常用来表示成功或失败。想返回字符串,得用echo输出,调用方用$(func)捕获。比如:get_date() { echo "$(date +%F)" } today=$(get_date)这里有个细节:函数里如果还有别的
echo调试信息,会一起被捕获。所以要么把调试信息打到stderr(echo "debug" >&2),要么老老实实用全局变量。错误处理是区分“能跑”和“可靠”的分水岭。很多人脚本开头不写
set -e,中间某条命令失败了继续往下跑,最后输出一堆莫名其妙的结果。set -e让脚本遇到非零返回就退出,set -u让未定义变量报错,set -o pipefail让管道中任意一环失败都算失败。这三个加一起,能挡掉八成低级事故。我现在的模板第一行永远是set -euo pipefail,除非有特殊理由不加。调试的时候,
bash -x script.sh比echo大法好用得多。它会打印每一行实际执行的命令,变量展开后的样子一目了然。如果输出太长,可以配合PS4='+ ${ BASH_SOURCE}:${ LINENO}: ',这样每行前面带文件名和行号,定位问题快很多。最后说个观察:很多人写shell脚本喜欢炫技,一行塞五个管道加三个
顶: 8踩: 28awk。过两周自己都看不懂。我的原则是——能拆成多行就拆,能加注释就加,变量名别用a、b、tmp。脚本是写给未来的自己看的,不是给机器看的。机器只关心语法,你关心的是半年后改需求时别想砸键盘。






评论专区