:I/O重定向和文本处理)
在「Linux常用命令学习笔记基础篇」里我们已经把cat、ls、less、wc、printf、find、alias这些单兵作战的命令摸了一遍。本篇是接续它的进阶篇重点讲两件事把命令连起来——用重定向和管道让一个命令的输出变成另一个命令的输入玩转文本——用一整套「文本处理三板斧」对文件做切片、排序、去重、搜索。学完这一篇你才算真正摸到 Linux 命令行的「组合技」门道。建议你配合基础篇一起读每个命令都自己敲一遍。前情回顾基础篇学过的本篇怎么用它cat配合做文件复制与重定向ls通过管道交给less/grep/tee二次处理lessls -la /etc | less分页看长输出wc进阶用-l/-w/-c取单项计数并引出nlprintf当管道的输入源喂给tr等工具find按「文件名/类型」找文件本篇grep按「内容」找文本正好互补aliassource ~/.bashrc理解环境变量的持久化思路异曲同工记住一个核心心法基础篇的命令多是「自己干自己的活」进阶篇的精髓是把它们用|、、串成流水线。一、I/O 三兄弟stdout / stdin / stderr先搞清楚什么是 I/O 流在 Linux 里每一条命令本质上都是一个「小工厂」它从某个地方拿原材料输入加工后把成品送到某个地方输出。这条「原材料进、成品出」的通道就叫做I/O 流Input/Output stream。我们日常敲命令时几乎感觉不到这些流的存在是因为系统给了它们一套默认规则输入默认来自键盘你敲什么命令读什么输出默认送到屏幕命令算出来的结果直接打印给你看。这种「默认行为」很方便但不够灵活。比如我想把命令的结果存成文件、想把 A 命令的结果直接喂给 B 命令、想把报错藏起来不让它刷屏……这时候就需要I/O 重定向redirect——也就是「指哪儿打哪儿」手动改变数据的来处和去处。进阶篇的 I/O 三兄弟正是三条最基础的数据流stdout标准输出命令的「正常结果」走这里stdin标准输入命令的「原材料」从这儿来stderr标准错误命令出错时的「报错信息」走这里。理解了这三样下面所有的重定向符号就都不用死记了。1.1 stdout 与、作用stdoutstandard output标准输出是命令「干完活交出来的成果」默认直接丢到终端屏幕上。而、是重定向操作符用来把这份成果「拦截」下来写进文件而不是显示在屏幕上——覆盖写、追加写。语法命令 文件 # 把 stdout 覆盖写入文件文件不存在则创建 命令 文件 # 把 stdout 追加到文件末尾不覆盖旧内容关键风险是「覆盖写」。如果目标文件已经存在它会被整个清空再写新内容——重要文件慎用想保留旧内容就用追加写。示例讲解echo Hello World # 不加任何符号结果直接显示在终端echo Hello World peanuts.txt # 把结果写进 peanuts.txt文件不存在则自动创建 cat peanuts.txt # 查看内容正是 Hello Worldecho Hello World peanuts.txt # 追加到末尾不会覆盖原有内容 cat peanuts.txt # 现在里面有 2 行 Hello World注意会完全覆盖原文件内容重要文件慎用想保留旧内容就用。1.2 stdin 与作用前面说的都是「去哪儿」输出stdinstandard input标准输入管的是「从哪儿来」。命令默认从键盘读输入而操作符用来告诉命令「别等键盘了去读这个文件吧。」就像给工厂换了原料供应商命令本身不用改只是原料来源变了。语法命令 文件 # 让命令从文件读 stdin而非键盘示例讲解经典的复制文件写法把「读文件」和「写文件」串在一起cat peanuts.txt banana.txt # peanuts.txt 让 cat 从文件读而非键盘 # banana.txt 把 cat 的输出写进新文件 # 结果peanuts.txt 的内容被复制到 banana.txt分解一下它做了什么 peanuts.txt把cat的 stdin 重定向到文件cat读取文件内容 banana.txt把 stdout 重定向到新文件。全程没碰键盘也没用中间变量纯靠重定向就完成了复制。1.3 stderr 与文件描述符作用命令其实有两条输出通道正常成果走stdout出错信息走另一条独立的stderrstandard error标准错误。前面学的只能拦 stdout拦不住 stderr所以报错照样蹦到屏幕上。为了精确控制三条流系统用文件描述符给它们编号其中2是 stderr 的专号用来专门「抓错误」。语法命令 2 文件 # 仅把 stderr 写入文件屏幕不再显示报错 命令 文件 21 # stdout 和 stderr 都进同一个文件 命令 文件 # 同上是上面的简写更省事 命令 2 /dev/null # 把 stderr 丢进「黑洞」彻底忽略报错文件描述符对照描述符流含义0stdin标准输入1stdout标准输出2stderr标准错误示例讲解先做个实验列出一个不存在的目录并试图把输出存进文件。ls /fake/directory peanuts.txt # 屏幕照样打印ls: cannot access /fake/directory: No such file or directory你可能会疑惑「我不是把输出到文件了吗怎么报错还在屏幕上」——因为只拦了 stdout拦不住 stderr。把错误单独存进文件屏幕立刻安静ls /fake/directory 2 peanuts.txt # 2 专门拦截 stderr 写进文件把正常输出和错误合并到同一个文件两种写法等价ls /fake/directory /etc/passwd peanuts.txt 21 # 先让 1 去文件再让 2 指向 1 的去处 ls /fake/directory /etc/passwd peanuts.txt # 是上面的简写更省事顺序很关键21必须写在 file之后意思是「把 stderr 指向 stdout 当前去的地方文件」。把错误丢进「黑洞」彻底无视报错屏幕干干净净ls /fake/directory 2 /dev/null # /dev/null 是个会吃掉一切数据的特殊文件二、管道|与tee让命令接力跑2.1 管道|作用前面我们用把命令结果存成文件再用另一个命令读文件——这要读写两次磁盘麻烦。管道|就是解决办法它把左边命令的 stdout直接接到右边命令的 stdin数据像水流一样在命令之间传递不用落地成文件。语法命令A | 命令B # 把命令 A 的 stdout 作为命令 B 的 stdin示例讲解ls -la /etc | less # 长列表太长直接交给基础篇学过的 less 分页看ls -la /etc会刷出一大片内容接上| less后结果不再哗啦全屏滚而是进了less让你慢慢翻。2.2tee—— 三通作用tee像一个「三通接头」它同时把数据往两个方向送——一边照常显示到屏幕一边写进文件。普通重定向是「二选一」要么屏幕要么文件tee是「我全都要」。默认是覆盖写加-a变追加写-i可忽略中断信号比如CtrlC。语法命令 | tee [选项] 文件名 # -a追加写入默认覆盖 # -i忽略中断信号 # 文件本身不存在 → tee 自动创建文件父目录不存在 → 报错tee 不会自建目录示例讲解ls | tee peanuts.txt # 屏幕看到文件也存一份 ls -la /etc | tee etc_listing.txt | grep conf # 存中间结果同时继续交给 grep 过滤第二条命令干三件事ls -la /etc列出目录tee etc_listing.txt存一份到文件同时把数据继续往后传grep conf从中筛出含 conf 的行。这条「tee中间插一道存盘、管道继续往下走」的模式在长命令链里特别好用。注意tee只会自动创建文件不会创建父目录目录不存在时会报错。三、环境变量env/PATH/export作用你的 Linux 系统用环境变量保存会话与配置信息比如你是谁、家在哪、命令去哪找。这些变量是「键值对」在变量名前加$即可取值对 shell 自己和它启动的程序都可见是系统与用户之间传递设置的主要方式。其中最核心的是PATH它是一串用冒号分隔的目录你敲的每条命令系统都去这些目录里翻可执行文件——所以「装了程序却 command not found」多半是它没在PATH里。语法env # 列出当前会话所有环境变量键值对清单 echo $变量名 # 查看某个变量的值如 echo $HOME、echo $PATH export 变量名值 # 为当前会话设置环境变量关掉终端即失效 export PATH$PATH:/目录 # 把自定义目录加进 PATH让命令随处可调用示例讲解3.1 查看环境变量echo $HOME # 主目录如 /home/pete echo $USER # 当前用户名 env # 列出当前会话所有环境变量键值对清单 echo $PATH # 一堆冒号分隔的目录命令就是去这些目录里找可执行文件3.2 PATH 的重要性echo $PATH # 输出类似/usr/local/sbin:/usr/local/bin:/usr/sbin:/bin假设你把一个程序装到非标准目录/opt/coolapp/bin直接敲它的名字会报command not found——因为PATH里没这个目录。解决办法是把目录加进PATH系统就能从任何位置找到它。3.3 为当前会话临时设置export TESTtest echo $TEST # 输出 test关掉终端就没了export让变量对当前会话及之后启动的子进程都可见。只要终端还开着变量就在一关终端就消失。3.4 永久生效写进启动文件想每次开终端都在思路和基础篇给ll设别名一模一样——写进 shell 启动文件nano ~/.bashrc # Bash 用 ~/.bashrcZsh 用 ~/.zshrcFish 用 ~/.config/fish/config.fish # 末尾加一行export TESTtest source ~/.bashrc # 立即生效不用重开终端# 顺手把自定义目录加进 PATH解决上面的 command not found export PATH$PATH:/你的目录如果自己装的程序报command not found多半是它所在目录没在$PATH里加进去即可。四、文本切片与拼接cut/paste4.1cut—— 垂直「剪」一列作用你可以把cut想象成一把垂直的剪刀给你一页文本它能顺着垂直方向把指定的「那一列字符」或「那一列字段」给剪出来。它有两种剪法按字符位置剪-c或按字段剪-f默认以Tab为「分隔墙」。文本若用逗号、分号等分隔就用-d自定义分隔墙。语法cut [选项] 文件 # -c 字符位置 按字符位置剪切如 -c 5 取每行第 5 个字符空格也算 # -f 列号 按字段剪切如 -f 2 取第 2 列默认以 Tab 分列 # -d 分隔符 自定义字段分隔符如 -d ;、-d ,示例讲解先造一个测试文件注意 lazy 和 dog 之间是一个真正的Tabecho The quick brown; fox jumps over the lazy dog sample.txt按字符位置剪空格也算一个字符cut -c 5 sample.txt # 输出每行第 5 个字符这里是 q按字段剪默认以 Tab 分列cut -f 2 sample.txt # 以 Tab 为界取第 2 列输出 dog用自定义分隔符这里用分号cut -f 1 -d ; sample.txt # 把分隔墙从 Tab 改成 ;取第 1 列 → The quick brown4.2paste—— 横向「拼」作用如果说cat是「竖着堆砖头」把文件一行行从上往下堆叠paste就是「横着排砖头」它把多行文本拉平、排成一行或者把多个文件按列并排拼起来。它默认用Tab作为连接符把各行之间的换行符\n抹掉换成连接符。语法paste [选项] 文件... # -s 把单个文件中的多行压成一行默认 Tab 连接 # -d 分隔符 自定义连接符如 -d 空格、-d ,示例讲解先准备一个多行文件touch sample2.txt cat sample2.txt # 粘贴下面四行后按 CtrlD 保存退出 The quick brown fox-s把单个文件的多行压成一行默认Tab连接paste -s sample2.txt # 输出The quick brown fox-d换掉默认连接符这里换成空格paste -d -s sample2.txt # 输出The quick brown fox五、看头看尾head/tail5.1head—— 看开头作用当文件很大比如系统日志cat /var/log/syslog会刷屏你往往只想快速瞄一眼开头几行head就是干这个的。它默认显示前 10 行用-n可以自定义行数。语法head [选项] 文件 # 默认显示前 10 行 # -n 行数 显示前 N 行如 head -n 15 file示例讲解head /var/log/syslog # 默认看前 10 行 head -n 15 /var/log/syslog # -n自定义行数看前 15 行5.2tail—— 看末尾还能实时跟随作用tail和head互补它看文件的末尾。默认同样显示最后 10 行用-n调行数。它最厉害的一招是-ffollow跟随——命令显示完末尾几行后不退出而是守着文件一旦有新内容追加就立刻刷出来。看实时日志全靠它。语法tail [选项] 文件 # 默认显示最后 10 行 # -n 行数 显示最后 N 行如 tail -n 20 file # -f 实时跟随文件末尾新内容追加即刷出CtrlC 退出示例讲解tail /var/log/syslog # 默认看最后 10 行 tail -n 20 /var/log/syslog # 看最后 20 行 tail -f /var/log/syslog # -f实时跟随文件有新内容就刷出来CtrlC 退出tail -f看日志神器插拔 U 盘、服务报错终端瞬间就刷新对应日志。六、Tab 与空格互转expand/unexpand6.1expand—— Tab 转空格作用不同编辑器里Tab显示宽度不一样导致文本对不齐、难阅读。expand的作用就是把文本里的Tab字符替换成固定数量的空格默认 8 个让格式统一。它只把结果打印到屏幕不改原文件。语法expand [选项] 文件 # 默认把 Tab 转成 8 个空格只打印、不改动原文件 # 结果通常配合 重定向保存如 expand file result.txt示例讲解先看看文件里真实的 Tab 在哪基础篇学过的cat -A会上场^I就是 Tabcat -A sample.txt # 输出示例The^Iquick^Ibrown^Ifox$expand sample.txt # 把 Tab 转成 8 个空格只打印不改动原文件 expand sample.txt | cat -A # 再 cat -A 验证^I 全部消失变成原生空格 expand sample.txt result.txt # 重定向保存结果6.2unexpand—— 空格转回 Tab作用反过来unexpand把空格转回Tab常用于减小文件体积或遵守要求用 Tab 的编码规范。它有点「保守」默认只转换每行行首的空格避免误把句子中间正常的单词间隔也压缩了加-a才放手把全文里所有凑满 8 个空格的地方都压成Tab。语法unexpand [选项] 文件 # -a 把全文所有凑满 8 空格的地方都压成 Tab默认只转行首缩进的空格 # 结果同样配合 重定向保存示例讲解unexpand -a result.txt # 反过来把空格转回 Tab-a 表示全文都转不加只转行首默认不加-aunexpand只转行首缩进的空格加了-a行中、行首所有 8 空格连块都压缩成Tab。七、合并与拆分join/split7.1join—— 按公共字段合并作用join类似数据库里的INNER JOIN内连接按一个公共字段把两个文件的行「拼」到一起。它的前提是两个文件都必须已经按连接字段排好序否则结果会错乱。默认按第 1 列连接如果公共字段不在第 1 列用-1 n/-2 n指定「文件1用第 n 列、文件2用第 n 列」。语法join [选项] 文件1 文件2 # 默认按第 1 列连接两文件须已排序 # -1 n / -2 n 指定文件1 / 文件2 用第 n 列作连接键 # -a 1 / -a 2 左 / 右连接保留无匹配的行两都加 -a 1 -a 2 为全连接 # -t 字符 自定义字段分隔符默认空白空格 / tab # -o 格式 自定义输出哪些列如 join -o 1.1,2.2 file1 file2示例讲解两个待合并的文件file1.txt file2.txt 1 apple 1 red 2 banana 2 yellow 3 orange 4 purplejoin file1.txt file2.txt # 输出 # 1 apple red # 2 banana yellow公共字段不是第 1 列时手动指定file1.txt file2.txt John 1 1 Doe Jane 2 2 Doe Mary 3 3 Suejoin -1 2 -2 1 file1.txt file2.txt # -1 2文件1用第2列-2 1文件2用第1列 # 输出 # 1 John Doe # 2 Jane Doe # 3 Mary Sue常用选项速查参数说明-1 n指定文件 1 使用第n列作为连接键-2 n指定文件 2 使用第n列作为连接键-a 1输出文件 1 所有行左连接即使关键词在文件 2 不存在-a 2输出文件 2 所有行右连接-a 1 -a 2输出两个文件全部行全连接-t 字符设置字段分隔符默认空白空格 / tab-o自定义输出哪些列如join -o 1.1,2.2 file1.txt file2.txtjoin -t , f1.csv f2.csv # -t指定分隔符为逗号处理 CSV7.2split—— 把大文件拆小作用split与join相反它把一个大文件拆成多个小片方便传输或分批处理。默认每1000 行一片文件名依次叫xaa、xab……可以用-l改行数、-b按大小拆。语法split [选项] 文件 [前缀] # 默认每 1000 行一片命名 xaa、xab… # -l 行数 按行数拆分如 split -l 500 file # -b 大小 按大小拆分如 split -b 10M file每片 10MB示例讲解split somefile # 默认每 1000 行一个文件命名 xaa、xab… split -l 500 somefile # -l自定义每片行数 split -b 10M somefile # -b按大小拆分如每片 10MB⚠️join要求两个文件已按连接字段排序否则结果会错乱。八、排序sort作用sort对文本行排序默认按**字典序ASCII**逐字符比较结果只打印到屏幕、不修改原文件。它最坑的一点字典序下10会排在2前面因为1 2所以遇到数字排序异常第一反应加-n按数值排。语法sort [选项] 文件 # 也支持管道命令 | sort # -n 按数值排序最常用 -r 倒序 / 降序 # -k 列号 指定按第几列排序如 sort -k 2 -n score.txt # -t 字符 指定字段分隔符默认空白空格 / tab # -u 去重只保留重复行的 1 份 # -o 文件 将结果写入文件可直接写回原文件 # -f 忽略大小写示例讲解file.txt内容为10和2sort file.txt # 默认输出 # 10 # 2 # 因为字符 1 2想要数字大小排序必须加 -n sort -n file.txt # 输出 # 2 # 10配合管道使用ls /etc | sort # 把 ls 的输出排序后再看 千万别写sort file file——会先把 file 清空再读什么都没了要写回用-o或管道给tee。九、字符转换tr作用trtranslate转换对字符做替换、删除、压缩常配合管道处理命令输出。它和sed/awk最大的区别是tr逐字符工作不认识单词、列或正则表达式——正因如此它在「改大小写、删数字、压空格」这类简单活上又快又省事。基本用法是把 SET1 里的每个字符映射到 SET2 相同位置的字符。语法tr [选项] SET1 [SET2] # -d 删除 SET1 中的字符 # -s 把连续重复字符压缩成 1 个 # -c 取反匹配「不在 SET1 中的」字符 # -t 转换前把 SET1 截断到 SET2 的长度 # 常用字符类[:lower:] [:upper:] [:digit:] [:alpha:] [:alnum:] [:space:] [:punct:]示例讲解基本字符转换echo hello world | tr a-z A-Z # 小写转大写 → HELLO WORLD echo 2026-06-23 | tr - / # 字符替换 → 2026/06/23 echo abc123 | tr abc ABC # 逐字符映射 → ABC123-d删除字符echo My address is 123 Main | tr -d 0-9 # 删数字 → My address is Main echo Hello, world! | tr -d [:punct:] # 删标点 → Hello world printf one\ntwo\nthree\n | tr -d \n # 删换行多行拼成一行 → onetwothree-s压缩重复字符echo Hello World | tr -s # 多个连续空格压成 1 个 → Hello World printf one\n\n\nTwo\n | tr -s \n # 压掉多余空行-c取反 -d只保留字母数字echo userx.com! | tr -cd [:alnum:] # 删掉非字母数字 → userexamplecom组合使用先删标点再压空格echo Hello,,, world!!! | tr -d [:punct:] | tr -s # → Hello world十、去重uniq作用uniqunique去重用来删除相邻的重复行。这里有个关键陷阱它只认「紧挨着」的重复行。如果重复行被隔开了uniq就看不见它们。所以实战里几乎永远和sort搭档——先排序让相同行相邻再去重。语法uniq [选项] 文件 # -c 统计每行出现次数 # -u 只显示不重复的行 # -d 只显示重复的行 # 常与 sort 搭配sort 文件 | uniq示例讲解reading.txt内容为book book paper paper article article magazineuniq reading.txt # 删掉相邻的重复行 # 输出 # book # paper # article # magazine统计 / 只看唯一 / 只看重复uniq -c reading.txt # -c统计每行出现次数 # 2 book # 2 paper # 2 article # 1 magazine uniq -u reading.txt # -u只显示不重复的行 → magazine uniq -d reading.txt # -d只显示重复的行 → book / paper / article重复行不相邻时要先 sortbook paper book paper article magazine articleuniq reading.txt # 因为没两行相同相邻什么都没删 sort reading.txt | uniq # 先排序让相同行相邻再去重 # 输出article / book / magazine / paper十一、统计与编号wc与nl11.1wc—— 字数统计作用wcword count做基础文件分析在文件上运行时它会输出「行数、单词数、字节数」三项摘要末尾跟文件名。wc在基础篇已经见过这里补它的单项计数用法。语法wc [选项] 文件 # -l 行数 -w 单词数 -c 字节数 # 不带选项时三项都输出行数 单词数 字节数 文件名示例讲解wc /etc/passwd # 输出行数 单词数 字节数 文件名 wc -l /etc/passwd # -l 行数 / -w 单词数 / -c 字节数11.2nl—— 给每行加行号作用nlnumber lines读取文件在每行开头加上行号后输出。它特别适合审脚本、看配置文件——一眼就能用行号定位内容。语法nl 文件 # 给每行加行号后输出默认从 1 开始、行号右对齐示例讲解file1.txt内容为i like turtlesnl file1.txt # 1 i # 2 like # 3 turtles十二、搜索利器grep作用grep是按内容在文本里找匹配行的搜索工具支持正则表达式。基础篇的find是按「文件名/类型」找文件而grep是按「内容」找文本——两者正好互补。它的真正威力在管道里可以过滤任何命令的输出。语法grep [选项] 模式 [文件] # 也支持管道命令 | grep 模式 # -i 忽略大小写 # -c 只统计匹配行数不显示内容 # -o 只输出匹配到的部分而非整行 # -e 模式 明确指定模式避免以 - 开头的模式被当成选项如 grep -e -v file # -f 文件 从文件读取多个模式示例讲解从文件里找包含 fox 的行grep fox sample.txt # 找包含 fox 的行常用选项grep -i pattern somefile # -i忽略大小写 grep -c fox sample.txt # -c只统计匹配行数 grep -o fox sample.txt # -o只输出匹配到的部分而非整行 grep -e -v file.conf # -e明确告诉 grep 后面是模式避免把 -v 当成选项 grep -f patterns.txt sample.txt # -f从文件读取多个模式管道里它才最猛——过滤任何命令的输出env | grep -i User # 从环境变量里筛出和 User 相关的 ls /somedir | grep \.txt$ # 配合基础篇的 ls筛出 .txt 文件提示正则表达式里.匹配任意字符\.才匹配真正的小数点$表示行尾。小结 下一步进阶篇我们完成了一次「质变」I/O 重定向2/dev/null把数据的来去攥在手里管道与 tee|让命令接力tee边看边存环境变量env/PATH/export并和alias一样靠~/.bashrc持久化文本处理全家桶cutpasteheadtailexpandjoinsplitsorttruniqnlgrep。这些工具单看平平无奇一旦用|串起来就能处理日志、清洗数据、批量整理文件——Linux 命令行的威力正在于此。如果在练手时想有个能随手敲命令的环境基础篇里推荐过的Linux Journeyhttps://labex.io/linuxjourney同样适合本篇边学重定向和管道边实操印象最深。 Linux 学习系列 · 全部文章Linux 常用命令学习笔记基础篇基础命令Linux 常用命令学习笔记进阶篇I/O重定向和文本处理Linux 常用命令学习笔记高级篇正则表达式与 Vim / Emacs 编辑器习笔记Linux 常用命令学习笔记用户管理篇用户、组与权限的钥匙Linux 常用命令学习笔记权限管理篇看懂 rwx管好每一份文件的生杀大权