Shell 的效率来自习惯而不是奇技淫巧。下面这些每天都会用到。
历史与快捷键
Ctrl+R # 反向搜索历史(再按 Ctrl+R 继续往前找)
!! # 上一条命令
sudo !! # 用 sudo 重跑上一条 —— 最常用的一条
cd - # 回到上一个目录
Ctrl+A / Ctrl+E # 行首 / 行尾
history | grep xxx 太慢,Ctrl+R 才是主力。
参数展开:一次处理多个目标
cp file.txt{,.bak} # 等价 cp file.txt file.txt.bak
mv report.{txt,md} # 改名
mkdir -p project/{src,test,doc} # 一次建三个目录
git diff HEAD~1 -- '*{.js,.ts}' # 多个模式
管道与退出码
command1 && command2 # 前者成功才执行后者
command1 || fallback # 前者失败才执行
cmd > out.log 2>&1 # 标准输出与错误都重定向
cmd 2>/dev/null # 丢弃错误输出(只在确定不需要时用)
echo $? # 上一条命令的退出码:0 才是成功
set -o pipefail 很关键:默认情况下 a | b 的退出码只看 b,前面失败会被吞掉。
脚本开头的三件套
#!/usr/bin/env bash
set -euo pipefail
IFS=$'\n\t'
| 选项 | 作用 |
|---|---|
-e |
任一命令失败就退出(否则后面会带着错误状态继续跑) |
-u |
使用未定义变量即报错(挡掉 $1 忘传这类问题) |
-o pipefail |
管道中任一环失败即视为失败 |
代价是 -e 会让“预期可能失败”的命令(如 grep 无匹配)中断脚本,此时用 cmd || true 显式豁免。
加引号:最便宜的正确性
rm -rf "$dir" # 目录名含空格时必须
[ -n "$var" ] # var 为空/未定义时不加引号会语法错误
for f in "$@"; do ... # 保留参数边界
用 shellcheck 能自动发现大部分引号与陷阱问题:
shellcheck script.sh
排查脚本的两个技巧
bash -x script.sh # 打印每条实际执行的命令(前面带 +)
bash -n script.sh # 只做语法检查,不执行
判据:能进版本控制、能被
shellcheck通过、失败时会退出——满足这三条,脚本才算能用。
