Shell 学习笔记
Claude Code 学习笔记
一、Shell 概述与选择
1.1 什么是 Shell
Shell 是操作系统与用户之间的命令行接口(Command-Line Interface, CLI)。它既是一种命令解释器,也是一种强大的脚本编程语言。用户在 Shell 中输入命令,Shell 将其解释并传递给操作系统内核执行,然后将结果返回给用户。
Shell 的本质
Shell 本质上是一个用 C 语言编写的程序,它是用户使用 Linux/Unix 系统的桥梁。Shell 既负责交互式地执行命令,也能够批量执行脚本文件中的命令序列。常见的 Shell 实现包括 Bash、Zsh、Fish、Dash 等。
1.2 Shell 的历史演进
Shell 的发展历程是 Unix/Linux 系统发展的一个缩影:
| 年代 | Shell | 开发者 | 特点 |
| 1971 | Thompson Shell | Ken Thompson | 最早的 Unix Shell,功能简单 |
| 1977 | Bourne Shell (sh) | Stephen Bourne | 引入变量、控制流,成为标准 Unix Shell |
| 1989 | Bash (Bourne Again Shell) | Brian Fox | 兼容 sh,增加行编辑、历史、补全等功能 |
| 1990 | Zsh (Z Shell) | Paul Falstad | 强大的补全、主题、插件系统 |
| 2005 | Fish (Friendly Interactive Shell) | Axel Liljencrantz | 开箱即用,语法高亮,自动建议 |
| 2016 | Dash (Debian Almquist Shell) | Herbert Xu | 轻量快速,POSIX 兼容,用于系统启动 |
核心事实:Bash 是目前 Linux 和 macOS 系统上的默认 Shell,也是绝大多数服务器环境的标准配置。Zsh 自 macOS Catalina (2019) 起成为 macOS 的默认 Shell。理解 Bash 就等于理解了绝大多数 Shell 环境下的工作方式。
1.3 主流 Shell 对比
| 特性 | Bash | Zsh | Fish | Dash |
| POSIX 兼容 | 是(默认模式) | 部分(sh 模拟) | 否 | 完全 |
| 脚本兼容性 | 极好(行业标准) | 良好 | 差(语法不兼容) | 极好 |
| 交互体验 | 良好 | 优秀 | 优秀 | 差 |
| 插件/主题 | 有限(bash-it) | 丰富(oh-my-zsh) | 内置 | 无 |
| 自动补全 | 基础 | 强大可编程 | 自动建议 | 基础 |
| 性能 | 中等 | 较慢(插件多时) | 中等 | 极快 |
| 适用场景 | 通用/脚本/服务器 | 开发环境 | 新手/桌面 | 系统脚本/启动 |
学习建议
初学者建议从 Bash 开始学习,因为 Bash 是 Linux 服务器的标准 Shell,掌握 Bash 后可以在任何 Linux 环境中工作。Zsh 在交互体验上更优,适合作为日常开发环境,但脚本编写仍建议遵循 Bash 语法以确保最大兼容性。
1.4 在 Windows 上使用 Shell
Windows 用户可以通过以下方式获得 Shell 环境:
| 方式 | 优点 | 缺点 | 推荐指数 |
| WSL 2 (Windows Subsystem for Linux) | 完整的 Linux 内核,原生性能,与 Windows 文件系统互操作 | 需开启虚拟化,占用磁盘空间 | 五星 |
| Git Bash | 安装 Git 时自带,轻量级,开箱即用 | 功能有限,部分命令缺失 | 四星 |
| Cygwin | 丰富的 Unix 工具移植 | 性能开销大,配置复杂 | 三星 |
| MSYS2 | 包管理,较新工具链 | 社区相对小 | 三星 |
| PowerShell | Windows 原生,支持 .NET 对象管道 | 语法与 Unix Shell 完全不同 | 二星(替代方案) |
1.5 查看和切换当前 Shell
以下命令用于查看和切换当前使用的 Shell:
echo "$SHELL"
cat /etc/shells
echo "$0"
bash --version
zsh --version
chsh -s /bin/zsh
chsh -s /bin/bash
$ echo "$SHELL"
/bin/bash
$ cat /etc/shells
/bin/sh
/bin/bash
/usr/bin/bash
/bin/zsh
/usr/bin/zsh
/bin/dash
$ bash --version
GNU bash,版本 5.2.15(1)-release (x86_64-pc-linux-gnu)
二、Shell 基础操作
2.1 终端打开方式
在不同平台上打开终端(命令行窗口)的方法:
| 平台 | 快捷键/方法 |
| Linux (GNOME) | Ctrl + Alt + T |
| Linux (KDE) | Ctrl + Alt + Space 或从菜单启动 Konsole |
| macOS | Cmd + Space 搜索 "Terminal" |
| Windows (WSL) | Win + R 输入 wsl |
| Windows (Git Bash) | 右键菜单 "Git Bash Here" |
| VS Code | Ctrl + ` (反引号) 打开内置终端 |
高效技巧
在 VS Code 中,可以使用 Ctrl + Shift + ` 创建新终端,Ctrl + Shift + 5 拆分终端面板。同时按下 Ctrl + D 可以关闭当前终端会话。
2.2 命令结构
Shell 命令的基本结构遵循统一的格式:
命令结构:command [options] [arguments]
- command -- 命令名,如
ls, cd, grep
- options -- 选项,用于修改命令行为,通常以
- (短选项) 或 -- (长选项) 开头
- arguments -- 参数,命令操作的目标
ls -la
cp -r /path/to/source /path/to/dest
grep -rin "pattern" ./
2.3 文件系统导航
掌握文件系统的导航是使用 Shell 的第一步:
| 命令 | 含义 | 示例 |
pwd | Print Working Directory,显示当前工作目录的绝对路径 | pwd → /home/user/projects |
ls | List,列出目录内容 | ls -la /home |
cd | Change Directory,切换目录 | cd ~/Documents |
pushd | 压入目录栈并切换 | pushd /tmp |
popd | 从目录栈弹出并切换 | popd |
dirs | 显示目录栈内容 | dirs -v(带序号) |
cd
cd ~
cd -
cd ..
cd ../..
pushd /var/log
pushd /etc
dirs -v
popd
popd
ls 命令详解
ls 是最常用的命令之一,常用选项:
-l -- 长格式显示(权限、链接数、所有者、大小、时间)
-a -- 显示所有文件(包括以 . 开头的隐藏文件)
-h -- 人类可读的文件大小(配合 -l 使用)
-t -- 按修改时间排序
-r -- 反向排序
-S -- 按文件大小排序
-R -- 递归列出子目录
2.4 文件操作
文件操作是 Shell 最基础也是最重要的功能:
| 命令 | 功能 | 常用选项 | 示例 |
touch | 创建空文件或更新文件时间戳 | | touch file.txt |
mkdir | 创建目录 | -p 递归创建 | mkdir -p a/b/c |
cp | 复制文件/目录 | -r 递归, -i 交互, -v 详细 | cp -rv src/ dest/ |
mv | 移动/重命名文件 | -i 交互, -v 详细 | mv old.txt new.txt |
rm | 删除文件/目录 | -r 递归, -f 强制, -i 交互 | rm -rf temp/ |
ln | 创建链接 | -s 符号链接(软链接) | ln -s target link |
危险命令警告
rm -rf / 会递归强制删除根目录下所有文件,导致系统崩溃。在生产环境中请务必小心使用 rm 命令,尤其是加上 -rf 选项时。建议:
- 删除前先用
ls 确认目标
- 在重要目录中使用
rm -i 启用交互确认
- 考虑使用
trash-cli 代替 rm (放入回收站而非直接删除)
2.5 查看文件内容
| 命令 | 用途 | 示例 |
cat | 连接并显示文件全部内容 | cat file.txt |
less | 分页查看(支持上下翻页、搜索) | less large.log |
more | 分页查看(仅支持下翻) | more file.txt |
head | 显示文件开头(默认 10 行) | head -n 20 file.txt |
tail | 显示文件末尾(默认 10 行) | tail -f app.log |
wc | 统计行/词/字符数 | wc -l file.txt |
tail -f /var/log/syslog
head -5 file.txt
tail -5 file.txt
grep -v '^\s*$\|^\s*#' script.sh | wc -l
2.6 文件权限管理
Linux/Unix 系统的文件权限模型是安全的基础。每个文件和目录都有三组权限(所有者、所属组、其他人),每组包含读(r)、写(w)、执行(x) 三个权限位。
ls -l
chmod 755 script.sh
chmod +x script.sh
chmod -w config.ini
chown user:group file.txt
chown -R user:group dir/
umask
umask 0022
权限速查表:最常见的权限组合及含义
-rwxr-xr-x (755) -- 程序文件,所有人可执行
-rw-rw-r-- (664) -- 数据文件,同组可写
-rw------- (600) -- 私密文件(如 SSH 私钥)
-rw-r--r-- (644) -- 普通文件默认权限
drwxr-xr-x (755) -- 目录默认权限
2.7 帮助系统
Shell 提供了完整的帮助系统:
man ls
man bash
man 5 crontab
ls --help
help cd
help source
type cd
type grep
whatis ls
apropos compress
man 页面导航
在 man 页面中,使用 Space 向下翻页,b 向上翻页,/pattern 搜索,q 退出。man man 可以查看 man 命令自己的帮助。
三、文本处理利器
文本处理是 Shell 最强大的功能之一。Linux/Unix 哲学强调"一切皆文件",而文本处理命令就是对文件内容的操作工具。
3.1 grep:文本搜索
grep (Global Regular Expression Print) 是 Shell 中最常用的文本搜索工具,它使用正则表达式对文本进行模式匹配。
grep "pattern" file.txt
grep "error" *.log
grep -i "error" log.txt
grep -v "debug" log.txt
grep -n "TODO" src/*.py
grep -c "function" script.sh
grep -r "main" ./src/
grep -l "config" ./src/*.js
grep -w "class" *.java
grep -B 5 "ERROR" log.txt
grep -A 10 "Exception" log.txt
grep -C 3 "failed" log.txt
grep 实用场景:
- 日志分析:
grep -E "ERROR|FATAL" app.log 快速定位错误
- 代码搜索:
grep -rn "function_name" --include="*.py" 查找函数定义
- 进程过滤:
ps aux | grep nginx 查找特定进程
- 配置检查:
grep -v "^\s*#\|^\s*$" config.conf 查看有效配置
3.2 sed:流编辑器
sed (Stream Editor) 是非交互式的流编辑器,主要用于对文本进行替换、删除、插入等操作。它一次处理一行内容,处理完一行后再处理下一行。
sed 's/old/new/' file.txt
sed 's/old/new/g' file.txt
sed 's/old/new/2' file.txt
sed 's/old/new/gi' file.txt
sed '1,10s/foo/bar/g' file.txt
sed '/^#/s/enabled/disabled/g'
sed '3d' file.txt
sed '5,10d' file.txt
sed '/^#/d' file.txt
sed '/^$/d' file.txt
sed '2i\插入的行内容' file.txt
sed '2a\追加的行内容' file.txt
sed -i 's/old/new/g' file.txt
sed -i '' 's/old/new/g' file.txt
sed -i.bak 's/old/new/g' file.txt
sed 命令格式详解
sed 命令的基本格式为:[地址范围]操作[参数]
- 地址范围:可以是行号(
5)、行号范围(1,10)、模式匹配(/pattern/)
- 操作命令:
s 替换、d 删除、p 打印、i 插入、a 追加
- 标志位:
g 全局、i 忽略大小写、p 打印
3.3 awk:文本分析工具
awk 是以三位创始人(Aho, Weinberger, Kernighan)命名的文本分析工具,擅长处理结构化文本(如按列分隔的数据)。
awk '{ print $1, $3 }' file.txt
awk '{ print $NF }' file.txt
awk '{ print NR, $0 }' file.txt
awk -F ',' '{ print $1, $2 }' data.csv
awk -F ':' '{ print $1 }' /etc/passwd
awk '/error/ { print }' log.txt
awk '$3 > 100 { print $1, $3 }' data
awk 'BEGIN { print "开始处理" } { sum += $1 } END { print "总和:", sum }' numbers.txt
awk '{ print FILENAME, FNR, $0 }' *.txt
ls -l *.log | awk '{ sum += $5 } END { print "总大小:", sum, "字节" }'
awk '{ ip[$1]++ } END { for (i in ip) print i, ip[i] }' access.log | sort -k2 -rn | head -10
awk '{ printf "%-20s %8s\n", $1, $2 }' file.txt
awk '$1 ~ /^[0-9]+$/ { print }' file.txt
3.4 sort, uniq, cut, tr, paste, join
| 命令 | 功能 | 常用选项 |
sort | 对文本行排序 | -n 数字排序, -r 倒序, -k 指定列, -u 去重 |
uniq | 去重(需要先 sort) | -c 计数, -d 只显示重复行, -u 只显示唯一行 |
cut | 按列/字符截取 | -d 分隔符, -f 列号, -c 字符范围 |
tr | 字符转换/删除 | -d 删除, -s 压缩重复, [:upper:] 等字符类 |
paste | 合并文件(按列拼接) | -d 分隔符, -s 串行合并 |
join | 按公共字段合并文件 | -t 分隔符, -1 文件1的字段, -2 文件2的字段 |
sort -k2 -n scores.txt
sort -t: -k3 -rn /etc/passwd
sort words.txt | uniq -c | sort -rn
sort access.log | uniq -d
cut -d: -f1,3 /etc/passwd
cut -c1-10 file.txt
cat file.txt | tr 'a-z' 'A-Z'
cat file.txt | tr -d '\r'
cat file.txt | tr -s ' '
paste -d, file1.txt file2.txt
3.5 diff 和 patch
diff 用于比较两个文件的差异,patch 用于将差异应用到文件。这两个命令是版本控制的基础。
diff file1.txt file2.txt
diff -u file1.txt file2.txt
diff -r dir1/ dir2/
diff -u old.py new.py > fix.patch
patch -p1 < fix.patch
patch -R < fix.patch
3.6 综合实战示例
awk '{ ips[$1]++ } END { for (ip in ips) print ips[ip], ip }' access.log \
| sort -rn | head -10
grep -rn "TODO\|FIXME\|HACK" --include="*.py" --include="*.js" ./src \
| awk -F: '{ printf "%-30s %-4s %s\n", $1, $2, $0 }'
for f in *.txt; do mv "$f" "${f// /_}"; done
awk '$0 ~ /^2026-05-04/ && $0 ~ /1[0-5]:/' app.log > morning.log
awk -F, 'NR>1 { sum[$2] += $3; count[$2]++ } END { for (k in sum) printf "%s: 总和=%d, 平均=%.2f\n", k, sum[k], sum[k]/count[k] }' sales.csv
四、输入输出与管道
4.1 标准输入/输出/错误
Linux/Unix 系统中,每个进程默认打开三个文件描述符:
| 文件描述符 | 名称 | 符号 | 默认目标 |
| 0 | 标准输入 (stdin) | < | 键盘 |
| 1 | 标准输出 (stdout) | > 或 1> | 屏幕 |
| 2 | 标准错误 (stderr) | 2> | 屏幕 |
理解 I/O 重定向
标准输出和标准错误都默认输出到屏幕,但它们是独立的。错误信息输出到 stderr 可以确保即使 stdout 被重定向到文件,错误仍然显示在屏幕上。
4.2 重定向操作符
command > file.txt
command >> file.txt
command < file.txt
command 2> error.log
command 2>> error.log
command &> output.log
command > output.log 2>&1
command >> output.log 2>&1
command > /dev/null
command > /dev/null 2>&1
cat file1.txt file2.txt > combined.txt
4.3 管道
管道 (|) 是 Shell 中最强大的特性之一。它可以将一个命令的 stdout 连接到另一个命令的 stdin,形成命令处理链。
管道哲学:每个命令做好一件事,通过管道组合成强大的处理流程。这正是 Unix 哲学的核心 -- "Do one thing and do it well"。
ps aux | grep nginx | grep -v grep | wc -l
ls -lhS | head -6
tail -f app.log | grep --line-buffered "ERROR"
cat file.txt \
| tr -cs '[:alpha:]' '\n' \
| tr 'A-Z' 'a-z' \
| sort \
| uniq -c \
| sort -rn \
| head -10
管道 vs 重定向
管道连接的是命令和命令,将一个命令的输出作为另一个命令的输入。
重定向连接的是命令和文件,将命令的输出写入文件或从文件读取输入。
两者可以混合使用:grep error log.txt | sort > sorted_errors.txt
4.4 tee:同时输出到文件和屏幕
command | tee output.txt
command | tee -a output.txt
command | tee output.txt | grep error
4.5 xargs:构建和执行命令
xargs 从 stdin 读取数据,将其作为参数传递给指定命令。这是处理大量文件时的利器。
find . -name "*.log" | xargs rm
find . -name "*.py" | xargs wc -l
find . -name "*.bak" -print0 | xargs -0 rm
seq 1 10 | xargs -I {} echo "Number: {}"
cat urls.txt | xargs -P 4 -I {} curl {}
xargs -n 2 < pairs.txt
4.6 Here Document
Here Document 是一种在脚本中嵌入多行文本的机制:
cat << EOF > greetings.txt
Hello, World!
这是一个多行文本。
EOF
mysql -u root -p << SQL
USE mydatabase;
SELECT * FROM users WHERE age > 18;
SQL
var="world"
cat << 'EOF'
Hello $var
EOF
if true; then
cat <<- END
This line is indented with tabs.
END
fi
五、Shell 脚本编程基础
5.1 脚本文件结构与 Shebang
Shell 脚本是以 .sh 结尾的文本文件,首行指定解释器路径:
echo "Hello, Shell!"
| Shebang | 含义 | 适用场景 |
#!/bin/bash | 使用 Bash 解释执行 | 通用脚本,兼容性最好 |
#!/usr/bin/env bash | 从 PATH 中查找 Bash | 可移植性更好(推荐) |
#!/bin/sh | 使用 POSIX Shell | 追求最大兼容性 |
#!/usr/bin/env python3 | 使用 Python 3 | 多语言脚本 |
脚本执行方式:
./script.sh -- 需要执行权限(chmod +x script.sh)
bash script.sh -- 不需要执行权限,作为 bash 参数
source script.sh 或 . script.sh -- 在当前 Shell 中执行
5.2 变量
name="John"
age=25
greeting="Hello, $name"
greeting2='Hello, $name'
echo $name
echo "${name}"
echo "${name}_suffix"
readonly PI=3.14159
unset name
echo "${var:-默认值}"
echo "${var:=默认值}"
echo "${var:?错误信息}"
echo "${var:+替代值}"
5.3 特殊变量
| 变量 | 含义 | 示例 |
$0 | 脚本名称 | ./script.sh |
$1, $2, ... | 位置参数(第 1 个、第 2 个...) | script.sh arg1 arg2 |
$# | 位置参数的数量 | 2 |
$@ | 所有参数(每个参数独立引用) | "arg1" "arg2" |
$* | 所有参数(作为单个字符串) | "arg1 arg2" |
$? | 上一条命令的退出码(0 成功,非 0 失败) | 0 |
$$ | 当前脚本的 PID | 12345 |
$! | 最后一个后台命令的 PID | 12346 |
$_ | 上一条命令的最后一个参数 | |
$LINENO | 当前行号(用于调试) | 42 |
5.4 字符串操作
str1="Hello"
str2="World"
result="${str1} ${str2}"
echo "${#str1}"
echo "${str:0:5}"
echo "${str:3}"
echo "${str: -3}"
echo "${str/old/new}"
echo "${str//old/new}"
echo "${str/#prefix/new}"
echo "${str/%suffix/new}"
filename="archive.tar.gz"
echo "${filename#*.}"
echo "${filename##*.}"
echo "${filename%.*}"
echo "${filename%%.*}"
echo "${str,,}"
echo "${str^^}"
5.5 数组
fruits=("apple" "banana" "cherry")
numbers=([0]=10 [1]=20 [5]=50)
echo "${fruits[0]}"
echo "${fruits[@]}"
echo "${#fruits[@]}"
fruits+=("date" "elderberry")
for fruit in "${fruits[@]}"; do
echo "$fruit"
done
declare -A user
user=([name]="John" [age]=30 [city]="Shanghai")
echo "${user[name]}"
5.6 算术运算
a=10
b=3
echo $(( a + b ))
echo $(( a - b ))
echo $(( a * b ))
echo $(( a / b ))
echo $(( a % b ))
echo $(( a ** b ))
let c=a+b
let d+=5
expr 10 + 3
echo "scale=2; 10/3" | bc
awk 'BEGIN { printf "%.2f", 10/3 }'
echo $(( ++a ))
echo $(( a++ ))
echo $(( a << 2 ))
echo $(( a & b ))
echo $(( a | b ))
六、流程控制
6.1 if/then/elif/else/fi
if [ "$age" -ge 18 ]; then
echo "成年人"
elif [ "$age" -ge 60 ]; then
echo "老年人"
else
echo "未成年人"
fi
if [ -f "$file" ]; then echo "是普通文件"; fi
if [ -d "$dir" ]; then echo "是目录"; fi
if [ -e "$path" ]; then echo "路径存在"; fi
if [ -s "$file" ]; then echo "文件非空"; fi
if [ -r "$file" ]; then echo "文件可读"; fi
if [ -w "$file" ]; then echo "文件可写"; fi
if [ -x "$file" ]; then echo "文件可执行"; fi
if [ "$str1" = "$str2" ]; then echo "相等"; fi
if [ "$str1" != "$str2" ]; then echo "不等"; fi
if [ -z "$str" ]; then echo "空字符串"; fi
if [ -n "$str" ]; then echo "非空字符串"; fi
if [ "$a" -eq "$b" ]; then echo "等于"; fi
6.2 test 命令和 [[ ]]
[ ] 与 [[ ]] 的区别:
[ ] 是 test 命令的简写,POSIX 兼容
[[ ]] 是 Bash/Zsh 的关键字,功能更强:支持 && || =~(正则匹配)
[[ ]] 不需要对变量加引号(自动处理空值)
- 可移植脚本用
[ ],Bash 专属脚本推荐 [[ ]]
if [[ "$str" == *.txt ]]; then
echo "是 txt 文件"
fi
if [[ "$str" =~ ^[0-9]+$ ]]; then
echo "全是数字"
fi
if [[ "$a" -gt 0 && "$b" -lt 100 ]]; then
echo "a 在范围内"
fi
if [[ "$user" == "admin" || "$user" == "root" ]]; then
echo "是管理员"
fi
6.3 case/esac
case "$1" in
start|Start)
echo "启动服务..."
systemctl start myservice
;;
stop|Stop)
echo "停止服务..."
systemctl stop myservice
;;
restart|Restart)
echo "重启服务..."
systemctl restart myservice
;;
status|Status)
systemctl status myservice
;;
*)
echo "用法: $0 {start|stop|restart|status}"
exit 1
;;
esac
6.4 for 循环
for color in red green blue; do
echo "颜色: $color"
done
for ((i=0; i<10; i++)); do
echo "计数: $i"
done
for file in *.txt; do
echo "处理文件: $file"
wc -l "$file"
done
for user in $(cut -d: -f1 /etc/passwd); do
echo "用户: $user"
done
arr=("one" "two" "three")
for item in "${arr[@]}"; do
echo "$item"
done
declare -A map=([a]=1 [b]=2 [c]=3)
for key in "${!map[@]}"; do
echo "$key = ${map[$key]}"
done
for i in $(seq 1 2 10); do
echo "$i"
done
6.5 while 和 until 循环
count=1
while [ "$count" -le 5" ]; do
echo "第 $count 次"
count=$((count+1))
done
while IFS= read -r line; do
echo "行: $line"
done < "file.txt"
until ping -c1 google.com &> /dev/null; do
echo "等待网络就绪..."
sleep 2
done
echo "网络已连接"
while true; do
echo "按 Ctrl+C 退出"
sleep 1
done
6.6 break 和 continue
| 关键字 | 作用 | 用法 |
break | 跳出当前循环 | break 或 break n(跳出 n 层循环) |
continue | 跳过本次循环的剩余部分 | continue 或 continue n |
line_number=0
while IFS= read -r line; do
line_number=$((line_number+1))
if [ -z "$line" ]; then
echo "第一个空行在第 $line_number 行"
break
fi
done < file.txt
while IFS= read -r line; do
[[ "$line" ==
echo "$line"
done < config.txt
循环模式选择
- for ... in ... -- 遍历已知集合(文件列表、数组等)
- for ((...)) -- 需要计数器的场景
- while read -- 逐行处理文件(最安全的文件读取方式)
- while true -- 守护进程、轮询等无限循环
- until -- 等待某个条件满足的场景
七、函数与作用域
7.1 函数定义与调用
function greet {
echo "Hello, $1!"
}
greet() {
local name="$1"
echo "Hello, ${name}!"
}
greet "World"
7.2 参数传递与返回值
print_info() {
echo "函数名: ${FUNCNAME[0]}"
echo "参数个数: $#"
echo "所有参数: $@"
echo "第一个参数: $1"
echo "第二个参数: $2"
}
print_info "foo" "bar" "baz"
is_even() {
if [ $(( $1 % 2 )) -eq 0 ]; then
return 0
else
return 1
fi
}
if is_even 42; then
echo "是偶数"
fi
get_username() {
echo "john_doe"
}
user=$(get_username)
echo "用户名: $user"
7.3 局部变量 local
global_var="我是全局的"
test_scope() {
local local_var="我是局部的"
global_var="函数中修改了全局变量"
echo "函数内: $local_var"
}
test_scope
echo "函数外: $global_var"
echo "函数外访问 local_var: $local_var"
作用域规则
- 不加
local 的变量默认是全局变量
local 只能在函数内使用
- 函数可以修改全局变量的值
- 函数的
$1, $2 等是函数的参数,与脚本参数隔离
7.4 函数库组织与 source
log_info() {
echo "[INFO] $(date '+%Y-%m-%d %H:%M:%S') - $*"
}
log_error() {
echo "[ERROR] $(date '+%Y-%m-%d %H:%M:%S') - $*" >&2
}
die() {
log_error "$*"
exit 1
}
confirm() {
read -r -p "$* [y/N]: " response
[[ "$response" =~ ^[Yy]$ ]]
}
source "$(dirname "$0")/lib/common.sh"
. "$(dirname "$0")/lib/common.sh"
log_info "脚本开始执行"
confirm "是否继续?" || die "用户取消了操作"
log_info "脚本执行完毕"
函数库组织最佳实践
- 将函数库放在
lib/ 目录中
- 使用
$(dirname "$0") 获取脚本所在目录
- 函数库中只定义函数,不执行代码
- 使用有意义的函数名前缀(如
log_, file_)
- 在函数库顶部添加使用说明注释
7.5 递归函数示例
factorial() {
if [ "$1" -le 1" ]; then
echo 1
else
local prev=$(factorial $(( $1 - 1 )))
echo $(( $1 * prev ))
fi
}
factorial 5
tree_list() {
local dir="$1"
local prefix="$2"
local files=("$dir"/*)
local i=0
for f in "${files[@]}"; do
local name=$(basename "$f")
local is_last=$(( i == ${#files[@]} - 1 ))
if [ -d "$f" ]; then
echo "${prefix}$([ "$is_last" = 1 ] && echo '└──' || echo '├──') $name/"
local new_prefix="${prefix}$([ "$is_last" = 1 ] && echo ' ' || echo '│ ')"
tree_list "$f" "$new_prefix"
else
echo "${prefix}$([ "$is_last" = 1 ] && echo '└──' || echo '├──') $name"
fi
i=$(( i + 1 ))
done
}
tree_list /path/to/dir
八、正则表达式
8.1 基本正则 vs 扩展正则
正则表达式有两种标准:基本正则表达式(BRE)和扩展正则表达式(ERE)。它们的区别在于一些元字符是否需要反斜杠转义。
| 元字符 | 基本正则 (BRE) | 扩展正则 (ERE) |
?(零次或一次) | 不支持(或 \?) | ? |
+(一次或多次) | 不支持(或 \+) | + |
{n,m}(区间量词) | \{n,m\} | {n,m} |
()(分组) | \(\) | () |
|(逻辑或) | 不支持(或 \|) | | |
实用建议:在 grep 中使用 -E 启用扩展正则,在 sed 中使用 -E 启用扩展正则。在脚本中使用 ERE 能减少转义字符,提高可读性。
8.2 元字符详解
| 元字符 | 含义 | 示例 | 匹配 |
. | 匹配任意单个字符(除换行符) | c.t | cat, cut, c0t |
* | 前一个字符出现零次或多次 | ab*c | ac, abc, abbc |
+ | 前一个字符出现一次或多次 (ERE) | ab+c | abc, abbc (不匹配 ac) |
? | 前一个字符出现零次或一次 (ERE) | colou?r | color, colour |
^ | 字符串开头 | ^# | 以 # 开头的行 |
$ | 字符串结尾 | \.py$ | 以 .py 结尾的行 |
[] | 字符集,匹配其中任意一个 | [aeiou] | 任意元音字母 |
[^] | 否定字符集 | [^0-9] | 非数字字符 |
() | 分组 (ERE) | (ab)+ | ab, abab, ababab |
| | 逻辑或 (ERE) | cat|dog | cat 或 dog |
{n,m} | 区间量词 (ERE) | [0-9]{3,5} | 3 到 5 位数字 |
\b | 单词边界 | \bword\b | 匹配完整单词 word |
\s | 空白字符(空格、Tab) | | |
\d | 数字(等价于 [0-9]) | | |
8.3 grep/egrep 正则示例
grep -E '^[A-Z]' file.txt
grep -E '\.(jpg|png|gif)$' files
grep -E '^[0-9]{3}-[0-9]{4}' data
grep -E '^(#|$)' config.ini
grep -E '\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b' file
grep -E '\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b' emails.txt
grep -E 'https?://[^[:space:]]+' file.txt
grep -P '\d{4}-\d{2}-\d{2}' log.txt
grep -P '(?<!\d)123(?!\d)' file.txt
8.4 sed 正则替换
sed -E 's/[[:space:]]+$//' file.txt
sed -E 's/^[[:space:]]+//' file.txt
sed -E 's/([0-9]{3})-([0-9]{4})/\1****/'
sed -E 's/^(.*)\.(.*)$/\2.\1/'
sed -E '/^#/d; /^$/d' config.ini
echo "hello world" | sed -E 's/(\w+) (\w+)/\2 \1/'
8.5 实用正则模式集锦
| 模式 | 说明 | 正则 |
| IP 地址 | IPv4 地址 | \b(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b |
| Email | 简单邮箱验证 | \b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b |
| URL | HTTP/HTTPS 链接 | https?://[^\s"'<>]+ |
| 日期 | YYYY-MM-DD | \b\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])\b |
| 中文 | 中文字符 | [\x{4e00}-\x{9fff}] (PCRE) 或 [一-龥] |
| 十六进制颜色 | #RGB 或 #RRGGBB | #([0-9a-fA-F]{3}|[0-9a-fA-F]{6})\b |
| 正整数 | 正整数 | ^[1-9][0-9]*$ |
| 浮点数 | 带小数位数 | ^-?[0-9]+(\.[0-9]+)?$ |
九、Shell 环境配置
9.1 配置文件加载顺序
Shell 在启动时会读取一系列配置文件。理解加载顺序对正确配置环境至关重要:
Bash 配置文件加载顺序
登录 Shell(如通过 SSH 登录、终端登录):
/etc/profile(所有用户的全局配置)
/etc/bash.bashrc(部分发行版)
~/.bash_profile(优先)或 ~/.bash_login 或 ~/.profile
非登录交互式 Shell(如在桌面打开终端、运行 bash):
/etc/bash.bashrc(部分发行版)
~/.bashrc
非交互式 Shell(如运行脚本):
- 读取
$BASH_ENV 指定的文件
最佳实践
将环境变量写在 ~/.bash_profile 中(因为它是登录 Shell 执行的,Graphical 模式下终端也通常会 source 它)。
将别名和函数写在 ~/.bashrc 中(因为所有交互式 Shell 都会读取它)。
在 ~/.bash_profile 中添加以下内容以确保登录 Shell 也会加载 ~/.bashrc:
if [ -f ~/.bashrc ]; then
. ~/.bashrc
fi
9.2 PATH 环境变量管理
PATH 变量定义了 Shell 在哪些目录中查找可执行文件。目录之间用 : 分隔。
echo "$PATH"
export PATH="$HOME/bin:$PATH"
export PATH="$PATH:$HOME/bin"
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc
if [[ ":$PATH:" != *":$HOME/bin:"* ]]; then
export PATH="$HOME/bin:$PATH"
fi
path_add() {
if [ -d "$1" ] && [[ ":$PATH:" != *":$1:"* ]]; then
export PATH="$1:$PATH"
fi
}
path_add "$HOME/.local/bin"
9.3 别名 alias 设置
alias rm='rm -i'
alias cp='cp -i'
alias mv='mv -i'
alias ll='ls -lah'
alias la='ls -A'
alias lt='ls -ltr'
alias ..='cd ..'
alias ...='cd ../..'
alias ....='cd ../../..'
alias home='cd ~'
alias grep='grep --color=auto'
alias df='df -h'
alias free='free -h'
alias ps='ps auxf'
alias gst='git status'
alias gl='git log --oneline --graph --all'
alias gd='git diff'
alias gp='git push'
alias -p
\rm file.txt
/bin/rm file.txt
unalias rm
9.4 提示符 PS1 定制
PS1 是 Shell 的主提示符,可以通过设置特殊转义序列来显示各种信息:
| 转义序列 | 含义 |
\u | 当前用户名 |
\h | 主机名(短格式) |
\H | 完整主机名 |
\w | 当前工作目录(完整路径) |
\W | 当前工作目录(仅最后部分) |
\d | 当前日期 |
\t | 当前时间(24小时 HH:MM:SS) |
\T | 当前时间(12小时) |
\@ | 当前时间(12小时 AM/PM) |
\n | 换行 |
\\$ | 普通用户显示 $,root 显示 # |
\[...\] | 包裹不可见字符(如颜色代码) |
export PS1='\u@\h:\w\$ '
export PS1='\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '
export PS1='\u@\h:\w\n\$ '
if [ -f /usr/share/git/git-prompt.sh ]; then
. /usr/share/git/git-prompt.sh
export GIT_PS1_SHOWDIRTYSTATE=1
export PS1='\u@\h:\w$(__git_ps1 " (%s)")\$ '
fi
9.5 历史命令管理
history
history 10
!123
!!
!$
!grep
^old^new
export HISTSIZE=10000
export HISTFILESIZE=20000
export HISTFILE="$HOME/.bash_history"
export HISTTIMEFORMAT="%Y-%m-%d %H:%M:%S "
export HISTCONTROL=ignoredups:erasedups
export HISTIGNORE="ls:cd:exit:pwd:clear"
shopt -s histappend
export PROMPT_COMMAND="history -a; $PROMPT_COMMAND"
9.6 任务控制
sleep 100 &
jobs
jobs -l
fg %1
bg %1
nohup long_running_task &
disown %1
9.7 实用 ~/.bashrc 配置模板
if [ -f ~/.bash_aliases ]; then
. ~/.bash_aliases
fi
export PATH="$HOME/.local/bin:$HOME/bin:$PATH"
export EDITOR=vim
export VISUAL=vim
export LANG=en_US.UTF-8
export LC_ALL=en_US.UTF-8
export HISTSIZE=10000
export HISTFILESIZE=20000
export HISTTIMEFORMAT="%F %T "
export HISTCONTROL=ignoredups:erasedups
export HISTIGNORE="ls:cd:exit:pwd:clear:history"
shopt -s histappend
export PROMPT_COMMAND="history -a;$PROMPT_COMMAND"
PS1='\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '
alias ll='ls -lah'
alias la='ls -A'
alias lt='ls -ltr'
alias grep='grep --color=auto'
alias ..='cd ..'
alias ...='cd ../..'
alias rm='rm -i'
alias cp='cp -i'
alias mv='mv -i'
mkcd() {
mkdir -p "$1" && cd "$1"
}
extract() {
if [ -f "$1" ]; then
case "$1" in
*.tar.bz2) tar xjf "$1" ;;
*.tar.gz) tar xzf "$1" ;;
*.bz2) bunzip2 "$1" ;;
*.rar) unrar e "$1" ;;
*.gz) gunzip "$1" ;;
*.tar) tar xf "$1" ;;
*.tbz2) tar xjf "$1" ;;
*.tgz) tar xzf "$1" ;;
*.zip) unzip "$1" ;;
*.Z) uncompress "$1" ;;
*.7z) 7z x "$1" ;;
*) echo "无法解压 '$1' - 未知格式" ;;
esac
else
echo "'$1' 不是有效文件"
fi
}
if [ -f /usr/share/bash-completion/bash_completion ]; then
. /usr/share/bash-completion/bash_completion
fi
配置文件修改后生效
修改 .bashrc 或 .bash_profile 后,需要重新加载才能生效:
source ~/.bashrc 或 . ~/.bashrc
exec bash -- 重新启动 Shell
- 关闭并重新打开终端
十、Shell 调试与最佳实践
10.1 调试模式
Shell 脚本提供了多种调试方式,帮助定位问题:
| 调试方法 | 说明 |
bash -x script.sh | 在执行前显示每条命令(带 + 前缀) |
bash -n script.sh | 只检查语法错误,不执行 |
bash -v script.sh | 显示输入的原始命令 |
set -x / set +x | 脚本中启用/禁用调试输出 |
set -e | 任何命令失败时立即退出(errexit) |
set -u | 使用未定义变量时报错(nounset) |
set -o pipefail | 管道中任何命令失败都视为管道失败 |
set -e
set -u
set -o pipefail
echo "调试开始"
set -x
result=$(some_complex_command)
set +x
echo "调试结束, result=$result"
trap 'echo "行 $LINENO: $BASH_COMMAND"' DEBUG
10.2 错误处理
if ! mkdir -p /some/directory; then
echo "创建目录失败" >&2
exit 1
fi
cleanup() {
echo "清理临时文件..."
rm -rf /tmp/myscript_* 2>/dev/null || true
}
trap cleanup EXIT
trap 'echo "被中断"; exit 1' INT TERM
trap 'echo "发生错误,行 $LINENO"' ERR
die() {
echo "[错误] $*" >&2
exit 1
}
require_cmd() {
if ! command -v "$1" &>/dev/null; then
die "需要 $1 命令但未找到,请先安装"
fi
}
require_cmd "curl"
require_cmd "jq"
10.3 安全编程实践
Shell 安全编程要点
- 始终引用变量:使用
"$var" 而不是 $var,避免空格导致的拆分
- 使用
set -euo pipefail 作为脚本头部
- 检查参数:总是在使用前验证输入参数
- 注意文件名中的特殊字符:使用
find -print0 | xargs -0 模式
- 临时文件使用
mktemp 而不是硬编码路径
- 避免 eval:eval 执行任意代码,尽量避免使用
set -euo pipefail
if [ "$#" -lt 1" ]; then
echo "用法: $0 <文件名>" >&2
exit 1
fi
input_file="$1"
[ -f "$input_file" ] || die "文件 '$input_file' 不存在"
tmpfile=$(mktemp /tmp/process_XXXXXX)
trap 'rm -f "$tmpfile"' EXIT
grep -E '^[0-9]+' "$input_file" > "$tmpfile"
mv "$tmpfile" "$input_file.processed"
echo "处理完成,已保存到 $input_file.processed"
10.4 性能优化建议
| 建议 | 说明 | 原因 |
| 减少外部命令调用 | 优先使用 Bash 内置功能 | 外部命令(如 grep、sed)需要 fork 进程,开销大 |
使用 $(( )) 替代 expr | 内置算术运算 | expr 是外部命令,$(( )) 是内置功能 |
使用 [[ ]] 替代 [ ] | Bash 关键字而非外部命令 | [[ ]] 是 shell 语法,[ ] 对应 test 命令 |
| 避免在循环中创建子进程 | 如避免 for i in $(cat file) | 命令替换会创建子进程,大文件时内存消耗严重 |
使用 printf 替代 echo | printf 更可靠 | echo 在不同平台行为不一致 |
| 避免过度使用管道 | 考虑使用 awk 代替多个 grep/sed 组合 | 每个管道阶段创建新进程 |
for word in $(cat words.txt); do
echo "$word"
done
while IFS= read -r word; do
echo "$word"
done < words.txt
awk -F: '/pattern/ { count[$2]++ } END { for (k in count) print count[k], k }' file \
| sort -rn
10.5 常见错误与陷阱
| 陷阱 | 错误示例 | 正确写法 |
| 变量赋值等号周围加空格 | name = "John" | name="John" |
| if 条件中缺少空格 | if ["$x" = "$y"] | if [ "$x" = "$y" ] |
| 忘记引用变量 | [ -f $file ] | [ -f "$file" ] |
test 中使用 < 或 > | [ "$a" > "$b" ] | [ "$a" -gt "$b" ] 或 (( a > b )) |
for 循环中使用 $(cat file) | for i in $(cat file) | while read -r i; do ... done < file |
忘记在 case 中使用 ;; | case $x in a) echo;; esac | 每个分支以 ;; 结束 |
| shebang 中错误路径 | #!/bin/bash(在非标准Linux) | #!/usr/bin/env bash |
使用 echo 处理选项 | echo "-n" 或 echo "hello\nworld" | 使用 printf |
10.6 ShellCheck 工具
ShellCheck 是一个 Shell 脚本静态分析工具,可以检测脚本中的常见错误、陷阱和不良实践。它被集成到许多编辑器和 CI 工具中。
安装 ShellCheck
- Linux:
apt install shellcheck 或 yum install shellcheck
- macOS:
brew install shellcheck
- Windows:通过 WSL 安装,或在 VS Code 中安装 shellcheck 扩展
- 在线:访问 shellcheck.net 粘贴代码分析
shellcheck myscript.sh
shellcheck --shell=bash myscript.sh
shellcheck --shell=sh myscript.sh
shellcheck -f json myscript.sh > report.json
shellcheck -f gcc myscript.sh
ShellCheck 常见警告代码:
- SC2086 -- 变量引用未加双引号(最常见的错误)
- SC2002 -- 无用的 cat(如
cat file | grep 改为 grep file)
- SC2046 -- 未加引号的命令替换(单词拆分问题)
- SC2162 -- read 命令缺少
-r 参数
- SC1091 -- 无法找到 source 的文件
十一、与 Claude Code 的结合
11.1 Claude Code 中 Shell 的使用场景
Claude Code 作为一个命令行 AI 编程助手,深度依赖 Shell 环境来完成各种任务。以下是 Shell 在 Claude Code 中的主要使用场景:
| 场景 | 说明 | 示例 |
| 文件操作 | 创建、读取、编辑、移动文件 | Read file.txt, Write output.txt |
| 命令执行 | 运行 Shell 命令完成任务 | Bash: git status, Bash: npm test |
| 文本搜索 | 在项目中搜索代码和文本 | 使用 Grep 工具在代码库中搜索 |
| 代码分析 | 统计代码行数、查找模式等 | find . -name "*.py" | xargs wc -l |
| 构建部署 | 运行构建脚本、部署命令 | Bash: make build && ./deploy.sh |
| Git 操作 | 版本控制操作 | Bash: git diff, git commit |
| 环境配置 | 安装软件、配置环境 | Bash: pip install -r requirements.txt |
Claude Code 中的命令执行机制:Claude Code 通过 Bash 工具执行 Shell 命令。每次 Bash 调用都在一个隔离的环境中运行,工作目录会重置到项目根目录。Shell 状态(如变量、环境设置)不会在命令之间持久化,因此每个命令应该是自包含的。
11.2 在 Claude Code 对话中执行 Shell 命令
Claude Code 通过内置的 Bash 工具执行 Shell 命令。使用场景包括:
ls -la src/
mkdir -p dist/assets
cp -r public/* build/
npm run build
python -m pytest tests/
cargo test -- --nocapture
cloc --exclude-dir=node_modules .
wc -l src/**/*.ts
grep -rn "deprecated" --include="*.py" .
git log --oneline -10
git diff --stat
git status
npm install lodash
pip install flask
go get github.com/gin-gonic/gin
安全注意事项
Claude Code 在执行 Shell 命令时遵循安全策略。某些命令可能需要用户确认授权:
- 文件修改操作通常需要权限确认
- 需授权后才能执行危险命令(如
rm -rf)
- 可以通过
update-config 技能配置权限白名单
- 建议将可靠的只读命令添加到 allowlist 以减少权限提示
11.3 Bash 工具在文件操作中的应用
Claude Code 的 Bash 工具在文件操作中发挥着重要作用。以下是一些在 Claude Code 工作流中常用的模式:
ls -la
find . -name "*.config.js"
tree -L 2 src/
grep -rn "import" src/ --include="*.ts"
grep -rn "TODO\|FIXME" . --exclude-dir=node_modules
for f in *.js; do mv "$f" "${f%.js}.mjs"; done
find src/ -name "*.py" | xargs wc -l | tail -1
du -sh dist/
在 Claude Code 中高效使用 Shell
- 描述性参数:使用
description 参数描述每个 Bash 命令的用途,方便阅读日志
- 后台任务:长时间运行的任务可以使用
run_in_background 参数,Claude Code 会在任务完成时通知
- 超时设置:长时间运行的任务可以设置
timeout 参数(最长 10 分钟)
- 分步执行:复杂的任务应该分解为多个 Bash 调用,而不是塞入一个长命令中
11.4 实用提示词模板
以下是一些在 Claude Code 中使用 Shell 的实用提示词模板:
提示词模板集锦
- 代码搜索:"在 src/ 目录下搜索所有包含 'useEffect' 的 .tsx 文件,显示文件名和行号"
- 批量操作:"将所有 .jpg 文件转换为 .webp 格式,保留原文件"
- 数据统计:"统计项目中的代码行数,按文件类型分组"
- 日志分析:"分析 app.log,找出最近 1 小时内的所有 ERROR 级别日志"
- 环境检查:"检查当前系统中 Node.js、Python、Git 的版本"
- 文件比较:"比较 dist/ 和 src/ 两个目录的差异"
- 项目初始化:"创建一个新的 React 项目,包含 TypeScript 和 ESLint 配置"
ls -la
cat package.json 2>/dev/null || cat Cargo.toml 2>/dev/null || ls *.py
npm outdated 2>/dev/null || cargo outdated 2>/dev/null || pip list --outdated 2>/dev/null
npm test 2>&1 | tail -20 || cargo test 2>&1 | tail -20 || python -m pytest 2>&1 | tail -20
grep -rn "TODO\|FIXME\|HACK\|XXX" src/ --include="*.{js,ts,py,rs}" 2>/dev/null | head -20
十二、核心要点总结
Shell 学习路线图
掌握 Shell 编程是一个循序渐进的过程,以下是建议的学习路径:
基础命令
→
文件操作
→
文本处理
→
管道重定向
→
脚本编写
→
流程控制
→
正则表达式
→
环境配置
→
高级技巧
12.1 每日必用命令速查
| 分类 | 命令 | 一句话说明 |
| 导航 | pwd | 显示当前路径 |
ls -la | 列出所有文件(含隐藏)的详细信息 |
cd - | 切换到上一个目录 |
| 文件 | cp -r src/ dest/ | 递归复制目录 |
mv old new | 重命名或移动 |
rm -rf dir/ | 递归强制删除(慎用!) |
ln -s target link | 创建符号链接 |
| 查看 | cat file | 显示文件全部内容 |
less file | 分页浏览(支持搜索) |
tail -f log | 实时监控日志文件 |
head -n 20 file | 显示前 20 行 |
| 文本 | grep -rn "pat" . | 递归搜索模式 |
sed -i 's/old/new/g' file | 全局替换并原地修改 |
awk '{print $1}' file | 打印第一列 |
sort \| uniq -c | 排序并统计频率 |
| 系统 | ps aux \| grep nginx | 查找特定进程 |
chmod +x file | 添加执行权限 |
df -h | 查看磁盘使用情况 |
12.2 三大黄金原则
原则一:引用所有变量
始终使用 "$var" 而不是 $var。即使 99% 的情况下不加引号也能工作,那 1% 的边界情况会让你追查数小时。使用双引号可以防止单词拆分和通配符展开。
原则二:启动脚本使用 set -euo pipefail
这是 Bash 脚本的"安全模式":
-e:命令失败时立即退出,避免错误蔓延
-u:使用未定义变量时报错,捕获拼写错误
-o pipefail:管道中任一步骤失败就视为整体失败
仅需 set -euo pipefail 这一行,就能避免 80% 的 Shell 脚本常见错误。
原则三:每个工具做好一件事,通过管道组合
Unix 哲学的核心:grep 搜索文本、sed 编辑文本、awk 分析文本、sort 排序。将它们通过管道组合起来,就能构建出强大的数据处理流水线。不要试图用单个工具完成所有事情。
12.3 命令速查组合模式
find . -name "*.log" -mtime -7 | xargs grep -l "ERROR" | xargs rm
awk '{print $1}' access.log | sort | uniq -c | sort -rn | head -10
cp config.ini config.ini.bak && sed -i 's/debug=false/debug=true/' config.ini && diff config.ini config.ini.bak
for f in *.jpg; do convert "$f" -resize 50% "thumb_$f" & done; wait
12.4 学习资源推荐
最终寄语
Shell 是开发者的基本功,也是生产力的倍增器。它可能看起来语法古怪、陷阱众多,但一旦掌握,你将获得对计算机系统的深度掌控能力。每一个高手都是从第一条命令开始积累的。每天多用、多查、多练,Shell 会成为你最得力的工具。
记住:在 Shell 的世界里,没有什么是几个管道不能解决的。如果不行,就再加一个。
27377