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 通过、失败时会退出——满足这三条,脚本才算能用。