ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Shell组合技实战:管道、重定向与日志分析一键搞定

Shell组合技实战:管道、重定向与日志分析一键搞定 Shell命令创意大赛玩转终端组合技先把一个命题说清楚Shell命令行到底怎么玩才不亏。很多人用了好几年Linux每天来来回回就是cd、ls、grep、vim这几个命令打转真遇到跨目录批量处理、日志分析、定时巡检这种活儿还是习惯性去找脚本、找工具甚至直接开个图形界面用鼠标点半天。我前阵子参加了一场内部的Shell创意赛规则很简单——只允许用命令行把指定场景的重复操作压到最短时间完成。整场比下来最大的感触不是谁记得的命令多而是谁更会把命令“串”起来。今天这篇就把我认为最值得收藏的组合思路、实操案例和踩坑记录整理出来希望能帮你打开一点思路。这篇文章适合谁刚接触Shell、想从“会敲命令”跨到“会玩命令”的新人写了不少脚本但经常绕弯路的开发者以及运维、数据分析岗位上每天要和文本、日志、批量操作打交道的朋友。整篇不会堆理论纯粹是实战向的内容核心就两个字——组合而且是以创意赛里那些“高分手速”为参照的实战组合。1. 内容整体设计与思路拆解1.1 组合技的三大基石管道、重定向与通配符Shell组合技听起来玄乎拆开看其实就三根柱子管道、重定向、通配符。管道|是Shell里最优雅的设计它把前一个命令的标准输出直接接给后一个命令的标准输入形成一个单向数据流。你可以把它理解成工厂里的流水线一台机器磨出面粉下一台机器接着和面再下一台负责成型每台机器只干一件事但串起来就是一条完整的生产线。比如我经常用的一行统计命令cat access.log | awk {print $1} | sort | uniq -c | sort -rn | head -20这条命令里cat读取日志awk抽出第一列IPsort排序uniq去重并计数再次sort做数值倒序最后head取前20名。每一步都简单到不用动脑子但连起来就能回答“今天哪些IP访问最频繁”这种问题。这就是组合技的基本形态——先拆解任务再用管道把子任务像积木一样拼起来。重定向、、、21解决的是数据去向问题。管道只负责把数据往下一个命令送但如果你想保存结果、追加内容、从文件读取输入就要靠重定向。这里有个容易忽略的点会覆盖文件是追加。我见过不少同事把重要的统计结果用单个写进文件第二次运行时发现之前的数据没了脸都绿了。所以但凡涉及历史记录、日志收集我默认都用只有明确要覆盖时才用。通配符*、?、[]、{}则是批量操作的神器。*匹配任意长度任意字符?匹配单个字符[]匹配字符集合{}可以做枚举展开。举个例子for f in /data/logs/2024-*.log; do echo 处理 $f; done这条命令会遍历所有以2024开头的log文件。通配符看起来简单但很多人忽略了一个细节通配符是在Shell层面展开的而不是命令本身去匹配。所以如果你写脚本时给变量加了双引号通配符就不会展开比如$files里的*就真的是个星号。这个坑后面我细说。1.2 为什么组合技比单条命令更值钱单条命令再强它也只是个孤立的动作和“解决问题”之间还差着几步。组合技的意义在于把孤立的动作用逻辑串起来让它变成一个可复用的流程。举一个最常见的场景磁盘快满了你要排查哪些目录占空间大。初级做法是敲df -h看一眼然后进目录里用du -sh *一个个看遇到大的再进去一层继续看。整个过程重复、手动、费眼神。组合技的做法是du -xh --max-depth2 / | sort -rh | head -30一条命令把整个排查流程走完从根目录开始统计所有子目录的占用按大小倒序排取前30个。少了大量重复操作不说输出结果一目了然。再比如你每天都要做同一件事检查某个服务是否还在运行并把结果记录下来。单条命令要做的事是执行ps查进程用grep筛选手动判断有没有结果把结果存到文件组合技可以一步到位ps aux | grep myapp | grep -v grep || echo myapp 挂了 /var/log/check.log||的意思是前一个命令失败比如grep没匹配到任何行退出码非0就执行后面的命令。一条命令同时完成“检测判断记录”三个动作。这种能力一旦上手你会不知不觉把很多手工操作都换成组合命令尤其是配合Shell的、||、;和()能实现的逻辑远远超出一般人的想象。2. 核心细节解析与实操要点2.1 高频命令的隐藏技能history、alias与快捷键组合技不是冷门命令大杂烩恰恰相反主力就是history、alias、CtrlR这些你天天见但不一定用透的东西。先说说history。很多人的印象是history就是列出历史命令然后被考古用。实际上它的正确玩法是配合grep找回“那条很长的命令”。比如你三天前跑过一条复杂的awk命令今天想再用但记不清了history | grep awk | grep access.log一行就能把目标命令捞出来然后直接复制或者用!事件号重放。这里有个更高效的操作!1234表示重新执行历史中编号为1234的命令!!表示执行上一条命令!$表示上一条命令的最后一个参数!*表示上一条命令的全部参数。这几个“感叹号魔法”我几乎每天都在用尤其是!$配合mkdir、cd这种场景极其顺手mkdir -p /data/project/logs cd !$!$展开成/data/project/logs直接进入刚建的目录少敲一次路径。再来说alias。它是一个“自定义快捷方式”把高频长命令压缩成短单词。但我更推荐在alias里就开始组合而不是简单映射。比如我常用的alias myipcurl -s ifconfig.me alias portsnetstat -tlnp | grep LISTEN alias freshhistory -c clear如果你发现某个组合命令一周要用三次以上就应该考虑给它起个别名别每次都重敲一遍。这是个典型的“磨刀不误砍柴工”操作。注意alias只在交互式Shell里生效脚本里如果你写了alias默认是不展开的写脚本时应该直接用函数或完整命令。还有一组容易忽略但效率极高的快捷键CtrlR反向搜索历史命令输几个关键字就能模糊匹配CtrlA跳行首CtrlE跳行尾CtrlW删除光标前一个词CtrlU删除光标到行首的所有内容。这些快捷键组合起来能让你在编辑长命令时几乎不碰方向键。练习一两天就能形成肌肉记忆效率提升非常明显。2.2 文本处理梦幻联动grep、awk、sed怎么接文本处理三剑客——grep、awk、sed——是Shell组合技里最常驻的选手。每个单拎出来都够写一本书但实战中真正高频的是它们各自几个核心用法和互相之间的配合。grep负责“筛选”。日常最常用的是grep -E扩展正则、grep -v反选、grep -c计数、grep -A/-B输出匹配行的后续/前文若干行。比如排查报错上下文时我会用grep -A 5 ERROR app.log把错误行以及后面5行都打出来上下文一目了然。注意grep默认是打印“匹配行”不加参数时正则里有些元字符要转义加了-E直接用就行省心。awk负责“提取字段”和“按条件处理”。它的基本语法是awk 条件 {动作}默认按空白符拆列$1、$2拿字段。实战里我最常用的是配合NR行号和FS字段分隔符。比如解析/etc/passwd时要把冒号当分隔符awk -F: {print $1, $6} /etc/passwd打印用户名和家目录。awk的动作里写任何变量和循环它本质上是一门精简的编程语言。sed负责“替换”和“按范围编辑”。最常见的是s/old/new/g替换注意不加g只替换每行第一个匹配。sed -n 10,20p可以打印指定范围的行。如果要把改动写回文件需要加-i参数。我见过太多同学在测试环境验证好sed命令后忘记加-i结果文件一点没变回头还找不到原因。三者配合起来的威力会更大。比如日志里先grep去掉无关行awk提字段再sed做格式整理grep ERROR app.log | awk {print $1, $2, $NF} | sed s/\[//g; s/\]//g这里grep筛出错误行awk打印时间戳和最后一个字段一般是错误码或信息sed把方括号清掉输出干净整洁。整个过程数据流是单向的每一步的输出正好是下一步的输入这也是组合技最有魅力的地方——你永远可以在中间插入一个新环节而不需要推翻整体。3. 实操过程与核心环节实现3.1 参赛作品一一行命令分析访问日志我当时的参赛任务之一是给定一份Nginx访问日志30秒内说出“访问量前五的IP、每个IP的请求方法分布、有没有状态码异常”。说实话如果靠眼睛看日志30秒连第一页都翻不完。但用组合命令全程就一条awk {print $1} access.log | sort | uniq -c | sort -rn | head -5输出结果直接就是“次数 IP”的倒序排列比如3402 192.168.10.24 2770 10.0.0.88 1751 172.16.3.15然后看请求方法分布先按IP过滤再统计grep 192.168.10.24 access.log | awk {print $6} | sort | uniq -c最后查状态码异常状态码在第9列取非2xx和3xx的计数awk {print $9} access.log | grep -E ^[45] | sort | uniq -c | sort -rn这3条命令加起来敲完也不会超过1分钟。但真正让我拿高分的不是命令本身而是我事先做了一件事把日志里无关字段先删了一版再分析避免大文件下重复读取。如果日志有好几百兆用head -1000先抽样试试或者用awk只取需要的列生成一个精简中间文件后面所有分析都在中间文件上做。实际生产环境里这条经验比任何花哨命令都实用。3.2 参赛作品二批量文件清理与归档脚本另一个题目是把7天前的日志文件压缩归档并按项目名分目录存放同时删除超过30天的压缩包。这个需求用Shell组合起来写成一个脚本就是批量操作的典型代表。先看核心代码#!/bin/bash # 要处理的日志目录 LOG_DIR/data/logs # 归档存放目录 ARCHIVE_DIR/data/archive cd $LOG_DIR || exit 1 # 1. 找到7天前修改、以.log结尾的文件压缩后移到归档目录 find . -name *.log -mtime 7 -print0 | while IFS read -r -d f; do project$(echo $f | cut -d/ -f2) mkdir -p $ARCHIVE_DIR/$project gzip $f mv $f.gz $ARCHIVE_DIR/$project/ done # 2. 删除归档目录里超过30天的.gz文件 find $ARCHIVE_DIR -name *.gz -mtime 30 -delete这里有几个细节值得展开。find配合-print0和while IFS read -r -d 的组合是为了处理文件名包含空格或特殊字符的场景。如果你直接写for f in $(find...)文件名一旦带空格循环就会把路径拆成好几段轻则处理错文件重则误删。这是Shell脚本新手最容易踩的坑之一也是我在比赛里被强调的得分点。cut -d/ -f2是用来提取项目名的。因为find . -name *.log输出的路径是./projectA/xxx.log这种按/切分后第二段正好是项目名。路径里有.是正常的cut不会把它单独当成特殊处理。如果你用awk -F/ {print $2}也一样完全看个人习惯。最后那行find ... -delete是三剑客的“远端表亲”它把查找和删除合到了一步比-exec rm {} \;效率高很多也避免了rm的参数列表过长问题。实际运维中我清理临时文件、过期日志全部用它。3.3 参赛作品三终端复用才是真正的组合技除了命令之间的组合终端本身也可以“组合”。我这次比赛里面最有感触的一个环节是tmux的现场演示。什么叫终端复用简单说就是一个终端窗口里开多个会话每个会话还保持着各自的运行现场。你可以在A会话里跑日志跟踪B会话里写脚本C会话里跑一个耗时很长的编译任务全部互不干扰。关键是——即使你断网、关掉终端窗口远程服务器上的会话依然在后台运行回来随时可以重新接上。最典型的场景你在服务器上执行一个需要跑40分钟的数据同步脚本期间网络一抖SSH断了脚本跟着进程就没了。用tmux跑网络随便抖重连后tmux attach直接回到现场一切照旧。这不是“可能有用”而是“迟早靠它救命”的功能。基本操作整理如下启动会话tmux new -s work给会话起个名字叫work分离会话按Ctrlb再按d会话转到后台终端回到正常状态重新接入tmux attach -t work列出所有会话tmux ls如果你发现自己经常会“SSH断了就要重新跑任务”那tmux应该立刻加入你的工具清单。这个工具本身不是Shell命令但它和Shell命令配合起来才能构成完整的工作流。3.4 把常用组合抽象成函数比赛最后一个环节是“提交一个对你日常工作最有用的组合方案”。我提交的是把动态巡检写成了一个函数放在~/.bashrc里health() { echo 负载 uptime echo 内存 free -h echo 磁盘 df -h | grep -E Filesystem|/$ echo 核心进程 ps aux | grep -E nginx|mysql|redis | grep -v grep }之后在任何终端里执行health就能一次性拉出一台服务器的健康状况。这个函数比alias更高级一点因为函数里可以写多行逻辑、局部变量、循环和条件判断。我个人的习惯是如果组合命令超过两个管道或者涉及条件判断就写成函数如果只是简单映射就用alias。写函数时有个细节要注意~/.bashrc里定义的函数默认只在交互式Shell里生效如果你在脚本中用bash script.sh方式运行外部函数是看不到的。需要的话可以在脚本里source ~/.bashrc或直接复制函数体。4. 常见问题与排查技巧实录4.1 中文乱码问题VSCode终端中文乱码、tail日志中文乱码这几个关键词经常被搜说明是个高频痛点。绝大多数场景下乱码的原因是终端编码和系统/文件编码不一致。Linux下系统通常默认UTF-8但有些工具或文件不是UTF-8比如Windows传上来的GBK编码文本这个时候在UTF-8的终端里显示必然乱码。排查三步走# 1. 看当前语言环境 locale | grep LANG # 2. 识别文件编码 file -i filename # 3. 临时切换语言环境再查看 LANGzh_CN.GBK cat filename如果文件是GBK而你希望转成UTF-8用iconv一步到位iconv -f GBK -t UTF-8 oldfile newfile注意输出要重定向到新文件iconv默认不支持直接写回原文件。生产环境里各类配置文件如果没有统一成UTF-8乱码问题会反复出现所以我的建议是在源头统一编码而不是每次都转换。4.2 脚本闪退与循环陷阱“Windows脚本命令闪退”是很多新手常遇到的问题。其实不光是WindowsLinux下脚本“什么都没干就退出”也很常见。一般就三种原因第一脚本第一行没有指定解释器#!/bin/bash导致系统不知道怎么执行第二文件没有执行权限错误提示不直观第三脚本里有语法错误被直接中断。排查方法很简单用bash -x script.sh把执行过程打印出来一目了然。-x参数会显示每条命令实际执行的是什么我曾经靠它修复过一个极其隐蔽的变量名拼写错误。另外如果脚本里用了set -e任何一条命令返回非0都会立即退出有时候你觉得“脚本闪退”其实是set -e在起作用。这时候看退出码并定位是哪条命令用set -x排查非常有效。循环陷阱也是一个重灾区。最典型的是for循环里的参数展开问题。比如files$(find . -name *.log) for f in $files; do echo $f done如果文件名有空格这里就会被拆成多个单词。务必使用find ... -print0结合while read -d 的方式。写Shell脚本时变量一定要加双引号除非你明确知道不加会怎样。这个习惯需要一遍一遍踩坑才能养起来。4.3 命令找不到或PATH环境问题command not found可能是每个人的Shell生涯里都会遇到的“老朋友”。如果你的命令明明装了却报找不到十有八九是PATH没有包含它的安装路径。比如你自己编译装了个程序放在/opt/mytool/bin直接敲工具名的时候Shell是不会认账的你需要export PATH/opt/mytool/bin:$PATH但注意这样导出只在当前Shell会话里有效关掉终端就没了。想永久生效要把这行写进~/.bashrc。排查命令实际路径用which或typewhich nginx type nginxwhich只管PATH里的文件type更强大它还能告诉你这是一个shell内置命令、别名还是函数。遇到“明明在A环境能用B环境找不到”的情况先对比两边的PATH。这个排查思路比瞎装环境要快得多。4.4 grep进程匹配到自己的经典问题如果你执行过ps aux | grep nginx大概率会看到输出的最后一行是grep自己root 12345 0.0 0.0 112712 964 pts/0 S 14:23 0:00 grep nginx这个时候你的脚本如果统计行数可能会多算一条。最稳妥的写法是加一层反向匹配ps aux | grep nginx | grep -v grep或者直接用pgreppgrep -f nginxpgrep -f会匹配完整命令行也不需要额外过滤自身。如果你不在乎这个多出来的进程行那什么都不用管但一旦你把它写进巡检脚本多出来的这条会让你误判服务还活着。这类“看似无害”的小细节才是Shell组合技里真正影响结果可靠性的地方。4.5 大文件日志分析的预处理技巧如果日志文件特别大直接用管道分析会“跑好久”。我通常的做法是先把大文件用head抽样或tail定位时间窗口再对抽样数据做试探性分析确定字段格式没变后才全量跑。如果确实需要全量分析可以用split把大文件切块多进程并行处理再合并结果split -l 1000000 access.log part_ ls part_* | xargs -P 4 -I{} sh -c sort {} | uniq -c {}.count cat part_*.count | sort -rn | head -20xargs -P 4表示同时起4个进程处理速度会明显提升。但要注意uniq只能处理相邻的重复行所以并行处理完的中间结果只能说明每个分块内的统计跨分块合并不一定准确需要再次排序合并。严谨一点的做法是最后把所有count文件再合并一次。实际场景中如果日志量到了这个量级更建议用专用日志分析平台Shell组合技应付日常中等规模足够了不必硬撑。写在最后几个个人习惯比赛结束之后我复盘自己用的那几十条组合命令发现真正高频的其实就是那十来个命令来回排列组合。历史命令里最常出现的工具是管道、awk、grep、sed、sort、uniq、find这些就够了。与其记一堆冷门命令的参数不如把常见的组合练熟练到不用思考就能用出来。我现在写复杂管道之前习惯先分步验证先跑第一步看输出确认没问题再往下接下一步。这个过程可能比直接闭眼写完再调试更快。而且分段验证能让你清楚地知道数据在每一环怎么流转的排查问题的时候能用二分法快速定位出错的环节。最后分享一个我最常用的小技巧把那些“稍微复杂一点但每次都能用”的组合命令写进~/.bashrc里全部定义成短函数。这样你的Shell会越长越顺手像自己的一把手边工具而不是一本枯燥的字典。命令行这个东西确实值得花点时间打磨它回报的是你往后每一天的终端时间。
返回列表