它们活在每一台服务器里,被每天唤起成千上万次,却固执地保留着几十年前的语法和脾气。

越是深入地熟悉Linux系统,就越发能够体会到一种十分奇妙的分裂感。
人们一方面在日常工作当中根本离不开这套强大的工具,可另一方面,又常常会在操作过程的某个不经意的瞬间,被它的别具一格的设计逻辑狠狠“坏”上那么一下。
这篇文章我就结合之前的经验,列举10个我认为比较坏的命令,当然了,命令本身没有好与坏,只是使用者的个人感受,仅供参考,不喜勿喷哈。
排在第一的,我认为是几乎每个Linux新手都绕不开的tar。它的别扭,从参数顺序就开始了。
$ tar -fxzv archive.tgztar: You must specify one of the '-Acdtrux' or '--test-label' options$ tar -xzvf archive.tgzf这个字母必须挨着文件名,你把它摆到xzv前面,tar就以为xzv是文件名,直接甩一句说没告诉它要干啥。所以文件名永远得贴在f屁股后面。
它比后来的减号加字母那套规矩还老,所以减号写不写都行,两套都认。
最有意思的是,现在的GNU tar会自己读文件头判断压缩类型,于是有时少写z也能解成。
等于锁的说明书上写转三下,偏有时转两下也开,这种不确定性比彻底拧不开还烦。它出来快三十年了还是这副脾气,估计没人敢动它。
接着说dd。别的命令好歹用减号加字母,它偏要用if= of= bs=这种赋值写法,猛一看像在写配置文件。
问题不在写法怪,在于它没有任何保护。
$ dd if=boot.img of=/dev/sda bs=4M# 没有进度没有确认,几秒后/dev/sda分区表就没了$ dd if=boot.img of=/dev/sdb bs=4M status=progressif是输入,of是输出,bs是每次搬多少。一个冷知识是,status=progress这个能看进度的选项,coreutils到2015年才加上。也就是说前面几十年,dd默认就是黑着屏盲跑,根本不知道它跑到哪了。
而且dd压根不是什么磁盘写入器,它本来是个通用转换引擎,大写转小写、ASCII和EBCDIC互转这种活儿都能干,只是大家最爱拿它干最危险的事,才混出名声。写错of,几秒就能把系统盘干没。
crontab -r这东西,值得单独拎出来说。编辑是crontab -e,清空是crontab -r,就差一个字母,后果天差地别。
$ crontab -r$ crontab -lno crontab for userr这玩意儿不跟你确认,啪一下所有定时任务全没了。cron这套从Unix早期就有,那会儿的信条是用的人自己清楚在干啥,加确认框算瞧不起用户。
后来的GNU版本补了个i让你确认,可默认关着。要防手滑,要么alias crontab='crontab -i',要么动手前先crontab -l > backup.txt存一份。这个习惯,就是被它打出来的。
chmod的数字模式,看着清爽,755,多干净。可755到底啥意思,得在脑子里现拆八进制,7是rwx,5是r-x。
$ chmod 755 script.sh# 拆开看:属主7=rwx,同组5=r-x,其他5=r-x$ chmod u=rwx,go=rx script.sh# 几乎没人学过的开头那个数字:$ chmod 1777 /tmp$ chmod 4755 /usr/bin/passwd前三位是4读2写1执行自由组合,可开头那个数字才是大多数人的盲区。1777里的1是粘滞位,4755里的4是setuid,管的都是特殊权限。
还有个冷门写法,chmod -R配大写的X,注意不是x,只给目录和本来就能执行的文件加执行权,批量改一整个项目比chmod -R 755稳当得多。这招见过不少干了五年的老手都不晓得。
find这家伙,查文件还行,一上exec,画风突变,结尾那个分号还必须转义。
$ find . -name "*.tmp" -exec rm {}find: missing argument to `-exec'$ find . -name "*.tmp" -exec rm {} \;$ find . -name "*.tmp" -exec rm {} +exec后面跟要执行的命令,{}是找到的文件占位符,结尾必须有个终止符。写;是每找一个文件就跑一次命令,写+是一批一批传进去。
删一万个临时文件,;要启动一万个rm进程,+可能就几十个,速度能差几十倍。真要删,更稳的是delete,find自带的,不用外头调rm,比exec rm既安全又快。
ps这命令身上同时跑着三套语法,一个人顶仨。
$ ps aux$ ps -ef$ ps -aux$ ps auxwwps aux是BSD风格,没减号。ps -ef是Unix风格,有减号。可ps -aux有时把x当成一个用户名去查,甩出来一屏幕看不懂的东西。
ps身上这三套规矩,Unix派必须带减号,BSD派故意不带,GNU派用双减号长选项。末尾加ww,命令列才不会被砍断。
想按内存排,用ps -eo pid,ppid,%mem,cmd --sort=-%mem,比默认那张表管用太多。同一命令三套规矩,偶尔还得两个都试一遍才踏实。
mv和rm最反人类的地方,是命令行里没有回收站。图形界面好歹能翻回来,这儿rm是物理删除,mv覆盖现成文件连个响都没有。
$ mv a.txt b.txt$ rm important.log$ rm -rf "$FOO/"前两句不提,第三句最阴。FOO要是没赋值,变空串,"$FOO/"就成了"/",整台机器的根目录进删除名单。
好在rm从2006年起默认开了--preserve-root,rm -rf /会被拦下,根保住了,可子目录照删不误。真想要回收站,装个trash-cli,alias rm='trash',删错还能捞。这东西,是误删第三次之后才肯装的。
systemctl思路没毛病,服务全收一个口子管,可执行起来碎得让人头大。单元名差一个字都不行。
$ systemctl status nginx.service$ systemctl daemon-reload$ systemctl --failed$ systemctl cat nginx$ systemd-analyze blamesystemctl管的是单元,名字得带类型后缀才稳,.service最常见。daemon-reload是你改了磁盘上的unit文件后,通知systemd重新读一遍,漏了这步改了白改。
顺手说两个好用的,systemctl cat能把服务最终生效的配置整页打出来,比去/etc/systemd翻片段直观。systemd-analyze blame直接告诉你开机哪个服务最磨蹭。可惜这俩藏得深,文档里不显眼。
sed的i就地改很爽,可Linux和macOS俩系统脾气不一样,脚本一跨平台就崩。
$ sed -i 's/foo/bar/' config.txt$ sed -i '''s/foo/bar/' config.txt$ sed -i.bak 's/foo/bar/' config.txt && rm *.bakGNU sed的i后面那个备份后缀可写可不写,所以-i 's///'能直接改。BSD和macOS的sed把i的参数当必填,你不给,它就以为config.txt是后缀,把命令本身当文件名,直接报错。
要两边都认,用sed -i.bak,各生成一份.bak再删。实在求稳别用i,老老实实sed 's///' f > f.tmp && mv f.tmp f。这坑写过跨平台脚本的基本都栽过。
最后说ip,它把用了十几年的ifconfig替了,也把老习惯替了。
$ ip addr show eth0$ ip link set eth0 up$ ip -br addr$ ip route get 8.8.8.8ip来自iproute2,跟内核走netlink通道,ifconfig来自更老的net-tools,走的是过时的ioctl。动词名词倒装是ip的特色,刚上手得倒着想。
试想一想,ifconfig不是简单过时,是已经半坏。内核后来不通过ioctl暴露某些网络状态了,于是ifconfig看不见辅助IP,读不全接口信息。
而ip route get 8.8.8.8能直接告诉你,去这个地址走哪张网卡、命中哪条路由,查网络比ping还直接。这招很多人到现在都没用过。
写到最后有点犹豫,要不要把大于号重定向误清空也算上。command > file会把已有的file直接截成0字节,坑过不少人。
最后没放,一是篇幅,二是它更像习惯问题,不是命令本身设计扭。
这些命令再烦,也没人真想回到没它们的日子。
它们拧的地方,多半是年头太久包袱太沉,改一下怕牵一发动全身。就盼着哪天哪个维护者心情好,给tar写个像样的说明,给dd默认加个进度。
改一样是一样,熬夜的人能少掉几根头发。要是也被哪条坑过,欢迎来评论区聊哈。
未经授权,禁止转载
关注、点赞、转发、评论

