【Linux从入门到精通】第6篇:管道符、重定向与通配符——命令行效率的核心秘诀
一、引言从“单兵作战”到“协同作战”到目前为止我们学习的每个命令都是独立使用的bashls -l grep error tail -f app.log这种用法能解决问题但远未发挥命令行的真正威力。Linux命令行的设计哲学是每个命令只做好一件事通过组合来完成复杂任务。组合靠什么三样东西重定向决定数据“去哪”和“从哪来”管道符让命令之间可以传递数据通配符用一个模式匹配多个文件这三者就像乐高积木的连接件把简单的命令拼接成强大的数据处理流水线。二、重定向控制数据的流向2.1 三个标准数据流每个Linux进程启动时系统会为它自动打开三个“文件”还记得第3篇讲的“一切皆文件”吗名称文件描述符默认指向含义标准输入0键盘程序从哪里读数据标准输出1终端屏幕程序正常输出到哪里标准错误2终端屏幕程序错误信息输出到哪里当你执行ls命令时它从标准输入键盘读取你的输入实际上ls不需要输入执行成功的结果写入标准输出屏幕如果出错比如目录不存在错误信息写入标准错误屏幕重定向的本质改变这三个数据流的默认指向让数据流向文件或其他地方。2.2 输出重定向 和 这是最常用的重定向操作。覆盖重定向bashecho Hello World output.txt这条命令的含义是把echo命令的标准输出重定向到output.txt文件。关键特性如果文件不存在会创建它如果文件已存在原有内容会被清空这是最容易踩的坑bashecho 第一行 test.txt echo 第二行 test.txt # test.txt里只有第二行第一行被覆盖了追加重定向bashecho 第一行 test.txt echo 第二行 test.txt # test.txt里有两行内容这是写日志的常用操作bashecho $(date): 服务启动成功 /var/log/myapp.log一个直观的类比像是“另存为”会覆盖原文件像是“追加内容到末尾”2.3 输入重定向输入重定向让命令从文件读取数据而不是从键盘bashwc -l file.txt # 统计file.txt的行数这相当于把文件内容“喂”给命令的标准输入。Here Document多行输入重定向这是一种特殊的输入重定向用加一个结束标记bashcat EOF config.txt server { listen 80; server_name localhost; } EOFEOF之间的内容会作为cat的输入最终被写入config.txt。这在脚本中生成配置文件非常实用。2.4 错误重定向2 和 21错误重定向专门处理文件描述符2标准错误。只重定向错误输出bashls /不存在的目录 2 error.log # 屏幕上什么也不显示错误信息写入了error.log分别重定向正常输出和错误输出bashls /存在的目录 /不存在的目录 output.txt 2 error.log # 正常输出到output.txt错误输出到error.log合并正常输出和错误输出这是最常用的错误处理方式——不管正常还是错误都输出到同一个文件bashls /home /nofile all.log 21语法解释21表示把文件描述符2错误重定向到文件描述符1标准输出当前指向的位置。合并输出的简写bash支持bashls /home /nofile all.log # 等同于 all.log 212.5 实战场景后台任务的输出管理假设你写了一个脚本backup.sh想在后台运行并记录所有输出bash# 完全静默丢弃所有输出 ./backup.sh /dev/null 21 # 记录日志不显示在屏幕 ./backup.sh backup.log 21 # 同时显示在屏幕并记录到文件用tee命令 ./backup.sh 21 | tee backup.log/dev/null是一个特殊的“黑洞”设备写入它的所有数据都会被丢弃。需要丢弃输出时用它。三、管道符命令之间的高速公路3.1 管道符的基本原理管道符|的作用是把左边命令的标准输出变成右边命令的标准输入。bashls -l | grep .txt数据流向textls -l → (标准输出) → | → (标准输入) → grep .txt3.2 管道是命令行效率的倍增器来看一个经典对比不用管道的做法bashls -l temp.txt # 先把列表存到临时文件 grep .txt temp.txt # 再从临时文件中过滤 rm temp.txt # 删除临时文件用管道的做法bashls -l | grep .txt管道省去了中间的临时文件让命令可以直接“对话”。3.3 多级管道搭建数据处理流水线管道可以无限串联形成处理流水线bash# 找出占用空间最大的5个文件 du -sh * | sort -hr | head -5 # 数据流向解析 # du -sh * → 计算每个文件/目录的大小 # sort -hr → 按人类可读格式反向排序大的在前 # head -5 → 取前5行bash# 查看某个服务进程的详细信息 ps aux | grep nginx | grep -v grep | awk {print $2, $11} # ps aux → 列出所有进程 # grep nginx → 过滤出包含nginx的行 # grep -v grep → 排除grep命令自身 # awk {print $2, $11} → 只输出PID和命令路径3.4 管道的局限与xargs的补充管道传递的是标准输出的文本但有些命令不接受标准输入作为参数它们只接受命令行参数。典型例子rm命令不接受标准输入你不能这样用bashfind . -name *.log | rm # 这行不通xargs的作用就是把标准输入的内容转换成命令行参数bashfind . -name *.log | xargs rmxargs读取管道传来的文件名列表然后构造命令rm file1.log file2.log file3.log ...常用xargs参数bash# -p执行前询问确认 find . -name *.log | xargs -p rm # -I {}指定替换位置 find . -name *.log | xargs -I {} cp {} /backup/{} # -n每次传递的最大参数数量 echo 1 2 3 4 5 | xargs -n 2 # 输出两行1 2 和 3 4 和 53.5 tee管道的“三通接头”tee可以在管道中间“分流”既把数据继续往下传又同时写入文件bash# 在屏幕上实时查看输出同时保存到日志 ./long_running_script.sh | tee output.log # 追加模式 ./script.sh | tee -a output.log这比单纯的重定向更灵活——你可以一边看进度一边记录日志。四、通配符用模式匹配多个文件通配符Wildcards让你用一个模式同时匹配多个文件名是批量操作的基础。4.1 *匹配任意长度的任意字符*是最常用的通配符它匹配零个或多个任意字符。bashls *.txt # 所有.txt结尾的文件 ls test* # 所有test开头的文件 ls *test* # 所有包含test的文件 rm *.log # 删除所有.log文件慎用*的边界情况*单独使用匹配所有文件不包括隐藏文件它不匹配以.开头的隐藏文件要匹配隐藏文件用.*4.2 ?匹配单个任意字符?只匹配恰好一个字符bashls file?.txt # 匹配 file1.txt, fileA.txt不匹配 file10.txt ls chapter??.pdf # 匹配 chapter01.pdf不匹配 chapter1.pdf4.3 []匹配字符集合中的单个字符[]用于指定一个字符集合匹配集合中的任意一个字符bashls file[0-9].txt # 匹配 file0.txt 到 file9.txt ls [abc]*.txt # 匹配以a、b或c开头的.txt文件 ls [A-Z]*.txt # 匹配以大写字母开头的.txt文件 ls [!0-9]*.txt # 匹配不以数字开头的.txt文件!表示取反常用字符集写法含义[0-9]所有数字[a-z]所有小写字母[A-Z]所有大写字母[a-zA-Z]所有字母[!0-9]所有非数字字符4.4 通配符的展开机制理解一个关键概念通配符是由Shell展开的不是由命令处理的。当你执行bashls *.txtShell在调用ls之前会先把*.txt替换成当前目录下所有匹配的文件名。如果当前目录有a.txt和b.txt实际执行的命令是bashls a.txt b.txt这个机制有两个重要含义命令本身不需要支持通配符——Shell帮你做好了替换如果没有匹配的文件*.txt会被原样传递给命令某些Shell可配置此行为4.5 通配符与管道、重定向的组合实战批量重命名文件配合for循环bash# 将所有.jpg文件改名为.png只是改后缀不转换格式 for file in *.jpg; do mv $file ${file%.jpg}.png done批量压缩特定文件bashtar -czf logs_backup.tar.gz *.log统计所有Python文件的代码行数bashwc -l *.py | tail -1只移动包含特定模式的日志文件bashmv *error*.log errors/五、综合实战构建一个系统巡检命令让我们把今天学的重定向、管道、通配符结合起来构造一条实用的系统巡检命令bash# 需求查看系统关键信息同时输出到屏幕和日志文件 { echo 系统巡检报告 $(date) echo echo --- 磁盘使用情况 --- df -h | grep -E ^/dev echo echo --- 内存使用情况 --- free -h echo echo --- CPU负载 --- uptime echo echo --- 最近5条错误日志 --- find /var/log -name *.log -type f 2/dev/null | xargs grep -i error 2/dev/null | tail -5 } 21 | tee -a system_check.log这条命令的构成分析{ ... }将多条命令组合成一个整体grep -E ^/dev只显示实际挂载的磁盘find ... 2/dev/null搜索日志文件权限错误丢弃xargs grep在找到的日志文件中搜索error21 | tee -a合并错误输出同时显示和记录六、本篇小结今天我们学习了命令行协作的三大利器重定向覆盖输出追加输出2重定向错误21合并输出/dev/null是数据黑洞管道符|连接命令前一命令的输出变成后一命令的输入多级管道形成数据处理流水线xargs弥补管道不能传递参数的局限tee在管道中分流数据通配符*匹配任意字符序列?匹配单个字符[]匹配字符集合中的单个字符Shell在命令执行前展开通配符记忆口诀大于号输出双大于追加二大于管错误二大于一合并它。竖线传数据tee管分流星号匹配多问号只一个方括号里选着来。动手练习bash# 1. 重定向练习 echo 第一行内容 practice.txt echo 第二行内容 practice.txt cat practice.txt # 2. 故意执行错误命令观察错误重定向 ls /nofile 2 error.txt cat error.txt # 3. 管道练习统计当前目录下有多少个.txt文件 ls *.txt 2/dev/null | wc -l # 4. 多级管道找出占用CPU最高的3个进程只显示PID和命令 ps aux --sort-%cpu | head -4 | tail -3 | awk {print $2, $11} # 5. 通配符练习 touch file{1..5}.txt file{A..C}.log ls file[0-9].txt # 只匹配数字结尾的 ls file[A-Z].log # 只匹配大写字母结尾的 ls *.{txt,log} # 同时匹配两种后缀 rm practice.txt error.txt file* # 清理七、下篇预告到目前为止我们一直在命令行中操作但有一个工具我们一直刻意回避——文本编辑器。对于Linux用户而言掌握一个终端下的编辑器是绕不开的必修课。下一篇我们将面对那个让无数新手“闻风丧胆”的编辑器——Vim。我会从“如何退出Vim”这个千古难题开始一步步教你从生存模式进阶到指法如飞。别怕跟着走一遍你会发现Vim没那么可怕甚至可能爱上它。延伸思考试试执行ls -l | grep .txt result.txt如果grep没有匹配到任何内容result.txt会是空的。你能解释为什么吗这与管道和重定向的执行顺序有关——Shell先准备好重定向目标文件清空它然后再启动管道中的命令。