Shell 真正强大的地方在于组合能力:一个命令产生数据,另一个命令过滤数据,再由重定向保存结果。理解这套模型,才能把临时操作变成脚本、CI 步骤、Agent 可执行的任务入口。

1. 管道

管道命令符把前一个命令原本要输出到屏幕的信息当作后一个命令的标准输入,其执行格式为「命令 A | 命令 B」。管道可靠的前提是:上游输出足够稳定,下游能正确理解输入格式。

# 输出禁止登录用户行数
grep /sbin/nologin /etc/passwd | wc -l
 
# 搜索与 bash 有关的进程
ps aux | grep bash

管道可以继续组合:命令 A | 命令 B | 命令 C。组合越长,越要注意可读性和失败定位;超过三四段后,通常应该考虑拆成脚本或保存中间结果。

这正是 Unix 哲学的核心体现——每个程序只做一件事,但通过管道将它们组合起来,就能完成复杂的任务。

2. 重定向

输入重定向是把文件导入命令,输出重定向是把原本要输出到屏幕的数据写入文件。它们是 Shell 自动化的基础,也是误覆盖文件的高风险区。

  • 标准输入重定向(STDIN,文件描述符为 0):默认从键盘输入,也可从其他文件或命令中输入。
  • 标准输出重定向(STDOUT,文件描述符为 1):默认输出到屏幕。
  • 错误输出重定向(STDERR,文件描述符为 2):默认输出到屏幕。

对于重定向中的标准输出模式,可以省略文件描述符 1 不写,而错误输出模式的文件描述符 2 必须明确。脚本里区分 stdout 和 stderr,是为了让结果、日志和错误能被不同系统接住。

输入重定向

符号作用
命令 < 文件将文件作为命令的标准输入
命令 << 分界符从标准输入中读入,直到遇见分界符才停止
命令 < 文件 1 > 文件 2将文件 1 作为命令的标准输入并将标准输出到文件 2
# 输入重定向:将 file1.txt 作为 sort 命令的标准输入
 
$ sort < file1.txt
 
# 输入重定向并重定向输出:将 file1.txt 作为 sort 命令的输入,并将结果输出到 file2.txt 中
 
$ sort < file1.txt > file2.txt
 
# Here 文档(<<):将两行 "This is a line." 作为 cat 的输入
 
$ cat << EOF
This is a line.
This is a line.
EOF

输出重定向

符号作用
命令 > 文件将标准输出重定向到一个文件中(清空原有文件的数据)
命令 2> 文件将错误输出重定向到一个文件中(清空原有文件的数据)
命令 >> 文件将标准输出重定向到一个文件中(追加到原有内容的后面)
命令 2>> 文件将错误输出重定向到一个文件中(追加到原有内容的后面)
命令 >> 文件 2>&1、命令 &>> 文件将标准输出与错误输出共同写入到文件中(追加到原有内容的后面)
# 输出重定向:将 "Hello, World!" 输出到 output.txt 文件中(覆盖文件原有内容)
$ echo "Hello, World!" > output.txt
 
# 错误输出重定向:将错误信息输出到 error.txt 文件中
$ ls /nonexistentdirectory 2> error.txt
 
# 追加输出重定向:将 "Hello again!" 追加到 output.txt 文件中
$ echo "Hello again!" >> output.txt
 
# 追加错误输出重定向:将错误信息追加到 error.txt 文件中
$ ls /anothernonexistentdirectory 2>> error.txt
 
# 同时重定向标准输出和错误输出,追加到同一文件
$ ls /nonexistentdirectory >> all_output.txt 2>&1
 
# 同时重定向标准输出和错误输出,追加到同一文件(简化写法)
$ ls /nonexistentdirectory &>> all_output.txt

3. 通配符

通配符用于模式匹配,方便批量操作文件。它的风险也在“批量”:删除、移动、改名之前先用 lsprintf '%s\n' 预览匹配范围。

# 使用 * 通配符:列出所有 .txt 文件
 
$ ls *.txt
 
# 使用 ? 通配符:列出所有只有一个字符的 .txt 文件
 
$ ls ?.txt
 
# 使用 [] 通配符:列出所有以小写字母开始的 .txt 文件
 
$ ls [a-z]*.txt
通配符含义
*任意字符
?单个任意字符
[a-z]单个小写字母
[A-Z]单个大写字母
[a-Z]单个字母
[0-9]单个数字
:alpha:任意字母
:upper:任意大写字母
:lower:任意小写字母
:digit:所有数字
:alnum:任意字母加数字
:punct:标点符号

4. 命令替换

命令替换允许将一个命令的执行结果嵌入到另一个命令中。Shell 提供了两种方式:

  • 反引号 `cmd`:传统写法
  • $(cmd):现代写法,两者等价
# 反引号方式
$ pushd `pwd`
 
# $() 方式
$ pushd $(pwd)
 
# 在 echo 中使用命令替换
$ echo "Today is `date`"
$ echo "Today is $(date)"

两者功能等价,但 $(...) 更易于阅读且支持嵌套,推荐使用。

5. 任务管理

Linux 系统中运行程序时,可以选择在前台或后台运行。在前台运行时,程序占用终端;后台运行时,程序继续执行但不占用当前输入。临时任务可以用后台管理,长期任务应该交给 systemd、supervisor、tmux、CI 或专门的任务系统。

后台运行

  • & 符号:在命令末尾添加 &,使命令在后台运行。
  • nohup 命令:使命令在后台运行并忽略挂断信号(SIGHUP),即使退出当前终端,程序仍会继续运行。
# 在后台运行
 
$ sleep 300 &
 
# 使用 nohup 后台运行,输出存入 nohup.out
 
$ nohup sleep 300 &
 
# 使用 nohup 并重定向输出
 
$ nohup sleep 300 > output.txt &

管理后台任务

# 列出当前终端会话中的后台任务
$ jobs
 
# 将后台任务切换到前台(假设任务编号是 1)
$ fg %1
 
# 将后台暂停的任务切换到后台继续运行
$ bg %1

查看和终止进程

# 查看系统中所有进程
 
$ ps aux
 
# 终止进程(通过 jobs 编号或 PID)
 
$ kill %1
$ kill PID
 
# 强制终止进程
 
$ kill -9 PID

6. 使用原则

stdout 是数据,stderr 是诊断:脚本和 Agent 都依赖这个边界。不要把进度日志混进要被下游解析的数据流。

先预览,再写入:通配符、重定向、rmmvxargs 组合前,先确认匹配范围和输出目标。

命令短不等于可靠:一行管道能解决问题,但如果不可读、不可调试、不可复用,就应该拆成有名字的脚本或任务。

后台任务要可回收&nohup 适合临时任务;长期运行的服务必须有日志、重启策略和停止入口。