1. 文件与目录:每天输入上百次的一套指令
1.1 定位与浏览:pwd、ls、cd的实际用法
看到标题是“Linux基本指令”,我估计不少人的第一反应是:这还有什么好写的?ls、cd、mkdir谁不会啊。但我在带新人和做技术分享时发现,恰恰是这些每天敲上百次的指令,藏着最多的使用误区。很多人能用Linux,靠的是死记硬背,而不是理解背后的设计逻辑,一旦遇到“命令没反应”“路径写错”“文件覆盖了”这类意料之外的情况,就完全不会排查了。
先说定位三件套pwd、ls、cd。pwd的作用是告诉你当前在哪个目录,这在写脚本、配路径时非常关键。我见过不少人在脚本里写相对路径,换了个目录执行就报错,最后用pwd一查才发现当前目录跟预期差了十万八千里。ls的常用参数就那几个:ls -l查看详细权限和修改时间,ls -a显示隐藏文件,ls -lh把文件大小转成人类可读的K、M、G单位。说句实话,在绝大多数排查场景里,ls -lh比裸ls有用得多,因为文件大小一眼就能看出异常——一个配置文件突然变成2G,八成是有进程在疯狂写日志。
cd本身没什么好讲的,但有两个习惯我想强烈安利。第一是cd -,这条指令可以直接回到上一次所在的目录,在A目录和B目录之间来回切换时效率极高,不用再手打一长串路径。第二是cd ~回到当前用户的家目录,这在多用户服务器上特别实用,因为不同用户的家目录路径完全不同,用~代替具体路径可以避免写死。很多人不知道,cd后面啥都不跟,效果跟cd ~一样,都是回家目录。
1.2 增删改移:cp、mv、rm以及我建议你养成的习惯
文件操作里,cp、mv、rm是三大主力,但它们的坑点也不少。
cp最容易忽略的是-r参数。复制目录时少了-r,系统会直接拒绝执行,报错信息叫“omitting directory”。新手经常看到这个报错就懵了,其实意思就是:你复制的是目录,需要加递归参数。另外,cp默认会在目标文件已存在时直接覆盖,不会询问。想让它安全一点,可以加-i参数启用交互确认,或者用-n参数实现“已存在就不覆盖”。我在管理服务器配置备份时,经常用cp config.yaml config.yaml.bak.$(date +%F)这种带日期后缀的备份方式,比直接覆盖安全得多,一旦改坏了还能立刻回退。
mv虽然跨文件系统时会变成“复制+删除”,但同分区内移动几乎是瞬时的,所以它的本质其实是“改路径”,不是“搬数据”。这个理解非常重要,因为它影响着你对“移动大文件为什么这么快”的判断。mv同样有覆盖问题,默认也是直接覆盖,所以我会在关键操作前先ls确认目标目录里没有同名文件。
rm是重点中的重点。rm -rf这条指令在所有Linux教程里都会被反复提及,核心就一句话:慎用,非常慎用。它在递归删除目录时不作任何确认,一旦删错,没有回收站可以找回。我自己的习惯是:对于重要数据,先mv到一个临时目录而不是直接rm;确认无误后,再执行清空操作。如果你用的是支持回收站的桌面环境,那还好;但在纯命令行的云服务器上,rm -rf删掉的目录可能真的就永远消失了。另外,rm -rf后面跟变量时要特别小心,如果变量为空,指令会变成rm -rf /,后果不堪设想。我在写清理脚本时会先加一行防御逻辑,校验变量非空再执行删除。
1.3 通配符与路径补全:为什么我很少敲完整文件名
还有两个提升效率的小工具,虽然严格来说不算“指令”,但几乎每天都在用。第一个是Tab键路径补全,这个真的推荐养成肌肉记忆。输入路径时按Tab,系统会自动补全,如果有多个匹配项就再按一次列出候选。用了补全以后,手敲长文件名错误率会直线下降,而且也不需要刻意记那些特别长的目录名了。
第二个是通配符。*匹配任意字符串,?匹配单个字符,[abc]匹配方括号中的任意一个字符。比如我想看当前目录下所有.log结尾的文件,直接ls *.log就够了;想删除文件名以temp开头、后面跟一个数字的文件,可以用rm temp?.txt。通配符的价值在于批量操作,配合cp、mv、rm使用,可以省去大量重复劳动。但要注意一点,通配符的展开是由Shell完成的,不是由命令本身完成的。也就是说,rm *.log这条指令在执行前,Shell已经把*.log替换成了所有匹配的文件名,如果当前目录没有匹配项,它会原样把*.log传给rm,结果就是报错“No such file or directory”。理解这一点,对调试脚本里莫名其妙的报错很有帮助。
2. 日志查看:tail、less、grep组合拳
2.1 tail -f与less:排查日志最常用的两个入口
如果让我选一个Linux下最常用的指令组合场景,那一定是查日志。无论是开发调试、运维排障,还是单纯想搞清楚某个服务到底干了什么,日志都是第一手信息。而查日志最常用的两个入口,我个人认为是tail -f和less。
tail -f会持续跟踪文件新增内容,文件一旦有新行写入就立刻显示出来。这个特性在跟踪应用输出、监控实时报错时非常好用。我第一次用tail -f跟踪应用日志时,直观感受就是“终于知道刚才那波用户请求到底发生了什么了”。tail默认只看最后10行,想看得更多可以加-n参数,比如tail -n 100 app.log。还有个实用技巧,tail -f支持同时跟踪多个文件,用tail -f file1.log file2.log即可,文件之间会用头部分隔,切换观察目标非常方便。
less则是另一种思路,它适合查看大文件。一次cat一个2G的日志文件,终端会被输出刷爆,而且体验极差;用less打开,它是分页加载的,速度很快,不会加载整个文件到内存。进入less界面后,按/可以搜索关键字,按n跳到下一处匹配,按Shift+G直达文件末尾。还有一点很多人不知道,在less里按F等价于tail -f的效果,可以实时跟踪文件内容,按Ctrl+C退出跟踪模式。等于说,less一个工具就能兼顾浏览和跟踪两种需求。
2.2 head与wc:日志头部与体量快速判断
head和wc是日志排查里容易被忽略但很实用的两个指令。
head -n 20 app.log可以看日志文件的开头部分,这在确认应用启动过程时特别有用。比如服务崩溃后重启失败,日志末尾往往只有一行“Failed to start”,但真正的原因可能在启动流程很前面的地方,此时先看头部,再看尾部,基本能判断出是环境问题还是服务本身的逻辑问题。
wc常用的是wc -l,统计文件行数。在排查“日志为什么增长这么快”的问题时,先用wc -l记录当前行数,隔一段时间再测一次,就能算出增长速度,再结合日志内容判断是正常业务流量还是异常循环。
2.3 从一次实际故障看日志排查顺序
说一个具体案例。有一回我负责的一个服务突然响应变慢,第一反应就是去查日志。我先用tail -n 50 app.log看了最近的内容,发现大量“Connection timed out”报错。然后我用less打开同一份日志,搜索关键字“timed out”,看看从什么时候开始出现这个报错,结果发现是从一次发布之后开始的。顺着这个线索,我对比了发布前后的配置,最终定位到是一个网络超时参数被调得太小了,线上环境网络波动稍微大一点就超时。
这次排查里,tail负责“当前发生了什么”,less负责“这个问题从什么时候开始的”,两个工具配合,问题很快收敛。如果你也经常和日志打交道,我的建议是:优先掌握tail -f、tail -n、less的搜索功能这三板斧,覆盖80%的日常场景。
3. 权限与用户:sudo、chmod、chown的误区与正确姿势
3.1 权限模型速览:r/w/x和数字表示
Linux的权限模型,本质上就三组权限位:所有者(user)、所属组(group)、其他人(other),每组有读(r=4)、写(w=2)、执行(x=1)三个权限,用数字表示时把三组数字拼起来就行。比如755,意思是所有者有读、写、执行权限,组和其他用户只有读和执行权限;644则代表所有者有读写,其他人只能读。
这个模型理解起来不难,但实际使用时容易犯两个错误。一是对“目录的执行权限”不理解。目录的x权限意味着“能否进入这个目录”,只给r权限虽然能看到目录里的文件名列表,但无法进入,这会让很多人困惑。二是对默认创建文件的权限不清楚。新建文件默认通常是644,新建目录默认是755,这背后其实是umask的默认值022在起作用,表示“屏蔽掉组和其他用户的写权限”。如果你希望新文件自带更严格的权限,可以通过修改umask来实现,比如umask 077会让新文件权限变成600。
3.2 chown与chmod的实用组合
实际使用中,chown和chmod经常配套出现。比如部署一个Web应用,代码文件属于root,但运行进程的用户是普通账户,如果权限设置不当,会出现“能看不能写”的情况。这时候先chown -R appuser:appgroup /data/app把目录归属调整好,再chmod -R 750 /data/app设置合适的权限,问题就解决了。
有个细节值得注意:chmod -R虽然能递归设置权限,但不同文件类型可能需要不同权限。比如一个项目里,目录需要755,脚本文件需要755,但普通配置文件只需要644。无脑chmod -R 777虽然能解决所有权限报错,但安全隐患很大,等于把系统所有文件都敞开给别人了。更合理的做法是分类处理:文件批量chmod 644,目录批量chmod 755,可执行文件单独给chmod 755。用find配合执行可以快速完成这种分类权限调整,比如find /data/app -type f -exec chmod 644 {} \;只改文件,find /data/app -type d -exec chmod 755 {} \;只改目录。
3.3 关于sudo我踩过的坑
sudo这个问题,我在文章里专门提一下。很多人以为sudo就是“临时变成root”,这其实是个危险的理解。sudo确实是让你以其他用户身份执行命令,通常是root,但它不是让你直接开一个root shell随便折腾,而是“只对当前这条命令提升权限”。比如sudo cat /etc/shadow可以查看密码文件,但紧接着的cat /etc/shadow还是以普通用户身份执行的。
我踩过的一个典型坑是:在脚本里用sudo执行了某个命令,因为脚本本身运行在普通用户下,而写脚本时忘了确认当前用户身份,结果日志路径、临时文件的归属全都变成了root,导致后续普通用户进程无法读写这些文件。所以我会在脚本开头加一句whoami或者检查$UID,确认当前执行身份,这能避免很多莫名其妙的权限问题。
3.4 umask与默认权限
umask值得单独讲一讲,因为很多人对它的理解只停留在“好像有个东西叫umask”。它的本质是“默认权限掩码”,即从默认权限里“扣除”哪些权限。Linux给文件的默认权限是666(即所有用户可读可写),给目录的默认权限是777(即所有用户可读可写可执行),但有了umask以后,实际创建的权限要减去umask值。
拿umask 022来说,文件的默认权限是666减去022得到644,目录是777减去022得到755。如果你的服务器希望新上传的文件默认不能给其他人读,就可以把umask改成077。我自己在配置新的Linux服务器时,会特意检查/etc/profile和/etc/bashrc里的umask值,因为不同的发行版默认值可能不同,这直接关系到后续所有新文件的权限表现。
4. 文本处理三件套:grep、awk、sed的实战用法
4.1 从管道符讲起:为什么把这些指令串起来
Linux命令行的魅力,很大程度上来自管道符|。它的作用是把前一条命令的输出,作为后一条命令的输入。这样我们可以把一系列小工具串联起来,像流水线一样处理数据。我第一次真正理解管道时,是看到一条这样的命令:ps aux | grep nginx | grep -v grep。它的逻辑是:先列出所有进程,再筛选包含“nginx”的行,最后把“grep进程本身”那一行排除掉。如果不加最后的grep -v grep,你会看到一个“正在搜索nginx的grep进程”,它也包含“nginx”字样,容易造成误判。
管道的价值在于组合,而不是单条命令的威力。grep负责筛选文本行,awk负责按列抽取数据,sed负责批量编辑文本,三者配合管道,几乎能处理所有日常文本任务。
4.2 grep:过滤与提取
grep是最常用的文本过滤工具,基本语法是grep 关键字 文件。我常用的参数有这么几个:-i忽略大小写,-n显示行号,-r递归搜索目录,-v反选(显示不匹配的行)。还有一个-E参数,表示使用扩展正则表达式,比如grep -E "error|exception" app.log可以一次匹配多个关键字,排查错误时特别方便。
grep的一个容易忽略的使用场景是“从命令输出里筛选”。比如我想看系统里所有Java进程,用ps -ef | grep java就够了,没必要先输出全部再肉眼找。再比如查某个端口被什么进程占用,一套组合拳下来很快能定位到像ss -tlnp | grep 8080这样的实用思路。不过注意,ss -tlnp需要适当权限才能看到进程名,普通用户可能只能看到端口占用状态。
4.3 awk:按列处理的思维
awk的入门门槛比grep高一点,但核心思想很简单:把文本按分隔符拆成列,然后对每一列做操作。默认分隔符是空白(空格和Tab),$0代表整行,$1、$2代表第一列、第二列,以此类推。
举一个高频场景:查看磁盘占用时,df -h输出的第一行是标题,后面是各分区的使用情况。如果我只想看“/dev/xxx”那个分区的使用率,可以这样写:df -h | grep /dev/xxx | awk '{print $5}',输出结果就是百分比的数值。这就是“先过滤,再提取列”的标准组合。
awk还可以做简单的统计。比如想统计日志里一共有多少行,wc -l就够了;但如果你想按某一列求和,比如统计一个访问日志里所有请求的总流量字节数,awk '{sum += $10} END {print sum}'就可以搞定。END块在数据处理完成后执行一次,这个模式在写统计类命令时非常常见。
4.4 sed:流式编辑
sed的定位是流式编辑器,它的特点是“按行读取、处理、输出”,适合对文件内容做批量替换。最常用的命令是s/旧内容/新内容/g,其中g表示全局替换。例如sed -i 's/old_version/new_version/g' config.yaml可以直接修改文件内容,把其中所有old_version替换成new_version。
这里特别要提醒的是-i参数,它表示“直接修改原文件”,不加-i的话,结果只会输出到终端,原文件不会变。很多新手第一次用sed时没加-i,看到终端明明有输出,但文件内容没变,就很困惑。倒过来,调试时也可以有意不加-i,先看看替换结果是否正确,确认无误后再加上-i真正执行。
sed还有一个常用场景是按行范围操作。比如sed -n '20,50p' app.log表示只打印20到50行,sed -i '30d' app.log表示删除第30行。虽然现在很多人写脚本更倾向用专门的工具,但sed在“临时改一下配置文件、批量替换几处内容”这个场景下,依然是最快的选择。
4.5 一个综合实战:分析Nginx访问日志
光讲用法不落地,很难真正内化。我说一个我经常用的综合案例:分析Nginx访问日志,找出访问量最高的10个IP。
假设访问日志每一行的格式是:IP地址在最前面,后面跟着时间和请求信息。我需要提取IP列并统计出现次数,命令可以写成:
awk '{print $1}' access.log | sort | uniq -c | sort -rn | head -10这条命令的逻辑拆开来看是这样:awk '{print $1}'把每行第一列(IP)抽出来;sort把相同的IP排到一起;uniq -c统计每个IP出现了几次,并在前面加上计数;sort -rn按计数从大到小排序;head -10取前10行输出。
我第一次跑这条命令的时候,最大的体会就是“原来一行命令可以顶一个简单的统计程序”。这个思路延伸到很多场景:统计哪个接口被调用最多、哪个用户操作最频繁、哪种报错出现次数最多,都可以套这个模式。类似的作业方式在你的日常工作中复用率极高,比现学现用更值得掌握。
5. 进程与资源排查:从看不懂到能定位问题
5.1 ps:找进程的三种方式
进程管理是Linux使用中的重要一环,最常碰到的就是“找到某个进程,看它在干嘛,然后决定要不要动它”。ps是查看进程状态的基础命令,但我推荐直接用ps aux或者ps -ef,因为这两个参数的组合会显示完整的进程列表,包括CPU占用、内存占用、启动命令等关键信息。
如果你要找特定进程,最好的办法不是直接ps aux然后用眼睛找,而是配合grep过滤。比如我想找所有和Python相关的进程:ps aux | grep python。这里有个实战细节:grep会把自己也匹配进来,所以习惯上再加一个grep -v grep排除自身,或者用pgrep指令直接按名字找进程号。pgrep -f python能直接列出对应进程的PID,更干净。
5.2 top与free:实时资源观察
当系统变慢,你首先应该看的不是某个业务进程,而是整体资源状况。top是实时刷新进程列表,默认按CPU占用排序,按下P键按CPU排序,按下M键按内存排序。我第一次用top时,习惯性地按下M看内存占用,结果发现一个Java进程占了内存的40%,那个服务的对象大小明显超过预期,顺藤摸瓜发现了代码里的一个集合没有设置初始大小,导致不断扩容。
free则是专门看内存的:free -h用人类可读单位显示总内存、已用内存、可用内存和缓存。要注意,Linux的内存统计里“available”才是真正可用的内存,而不是简单的“free + buffers/cache”。这一点比较反直觉,但很重要。当你怀疑内存不足时,重点看available,而不是只关注free。
5.3 kill的完整形态:kill、killall、pkill
kill这个名字让人误以为它的作用就是“杀死进程”,其实更准确的说法是“向进程发送信号”。最基本的用法是kill PID,默认发送的是TERM信号(编号15),相当于“请优雅地退出”。如果进程不响应,可以升级信号kill -9 PID,发送KILL信号,这个信号由内核直接处理,进程没有机会做清理工作,属于强制终止。
这里有个实战原则:先试TERM,再试KILL。直接kill -9相当于拔电源,进程来不及保存数据、释放资源,可能会留下临时文件或脏数据。我自己在重启服务前,一般先发TERM信号,等几秒,如果进程还活着,再考虑KILL信号。
killall和pkill可以按名字结束进程,比如killall nginx、pkill -f app.jar。pkill -f会匹配完整命令行,是用来定位“通过命令行参数指定的进程”的好选择。但是在生产环境批量结束进程前,最好先pgrep预览一下匹配到的进程列表,确认没有误伤其他进程。
5.4 一次CPU飙高的排查记录
我印象最深的一次排查,是某台服务器CPU持续飚到90%以上。我先用top看到底是哪个进程占的,结果发现某个Java进程排在最前面,CPU占用超过800%(多核情况下单进程能超过100%)。然后用ps -Lp 进程号 -o pid,tid,pcpu,comm查看这个进程的线程维度,发现其中几个线程CPU使用率特别高。接着用jstack(Java自带的线程快照工具)导出线程栈,把线程号转成十六进制,在线程栈里搜索对应的栈信息,最后定位到是一个死循环逻辑。
整个过程,真正用到的Linux基础指令其实就那几个:top、ps、grep。这印证了一个观点:基础指令虽然不用背全,但一旦你掌握了它们的组合逻辑,解决复杂问题的能力会立刻上一个台阶。
6. 后台运行与服务管理:让任务脱离终端
6.1 nohup与&:最基本的后台运行
很多时候我们希望在退出终端后,任务还能继续跑。这里有两个概念要分清:&是把任务放到后台运行,但它仍然依附于当前终端会话;nohup则让命令忽略挂断信号,终端退出后进程也不会被终止。两者经常组合使用,写成nohup 你的命令 &,这样任务既能脱离终端,又能在后台运行。
这套组合有一个细节:标准输出和错误输出默认都写到nohup.out文件里。想指定输出文件,可以这样写:
nohup java -jar app.jar > app.log 2>&1 &其中2>&1的意思是把标准错误(编号2)重定向到标准输出(编号1)当前指向的地方,也就是app.log。这样操作的目的是让报错信息和正常日志都进同一个文件,排查问题时不用分两个文件看。这个技巧如果你之前不知道,可以马上记下来,因为它几乎每次启动后台任务都用得上。
6.2 再谈tmux:比nohup更可靠的会话管理
如果你经常需要在远程服务器上跑长时间任务,比如数据迁移、模型训练、批量脚本,我推荐你花半小时学一下tmux。它和nohup的思路完全不同:tmux创建的会话是独立的,即使SSH断开,会话里的进程依然继续运行;下次登录后,tmux attach就能重新连回那个会话,屏幕上的内容、滚动历史都还在。
我个人的使用习惯是:每个任务开一个单独的tmux窗口,用Ctrl+B加N和P在窗口之间切换。更重要的一点是,tmux里能直接看到任务进度,不像nohup那样只能读日志文件。对于需要盯输出的任务,这个体验差距非常大。同样是后台运行,nohup更偏“跑完就行”,tmux更偏“我还能随时回来看一眼”。按需选择,没有绝对的对错。
6.3 systemctl:现代Linux服务的标准管理方式
如果你用的是带systemd的现代Linux发行版(现在绝大多数都是),那管理服务的第一选择应该是systemctl。常用命令就那几个:systemctl start nginx启动服务,systemctl stop nginx停止服务,systemctl restart nginx重启服务,systemctl enable nginx设置开机自启,systemctl status nginx查看服务状态。
systemctl status输出的信息比很多人想象的丰富,它不仅告诉你服务是active还是failed,还会显示最近几条日志记录。排查服务启动失败时,先systemctl status看一眼,再journalctl -u 服务名 -n 50查看这个服务最近的50条日志,基本上能覆盖80%的排查路径。journalctl -u 服务名 -f还可以实时跟踪某个服务的日志输出,比跑到日志文件里tail -f更精准。
我刚开始从service命令切换systemctl时,最大的不适是它给的报错信息太“友好”了,反而把根因盖住了。比如一个服务启动失败,状态可能只写了“failed”,但真正的错误原因在journal日志里。所以要养成“status只看状态,具体原因去journal里查”的习惯,而不是一看到failed就发懵。
还有一个我用了很久才养成的好习惯:每次修改service的配置单元文件后,要先执行systemctl daemon-reload,让systemd重新加载配置,再重启服务。如果不做这一步,你改了半天配置文件,重启后服务用的还是旧配置,这种“改了没生效”的坑最容易浪费大量时间。在折腾自定义服务时先记下这个步骤,能少走很多弯路。
最后聊一点自己的体会。Linux基本指令这套东西,真不是靠背参数表能学会的,因为指令之间的关系是网状而不是线性的:ps配合grep才能精确找进程,awk配合sort和uniq才能做统计,nohup配合重定向才能干净地跑后台任务。我见过最快的成长路径,就是在真实的故障场景里用它们,遇到问题去查手册、看示例,然后立刻在机器上试。用得多了,那些指令会慢慢变成你的肌肉记忆。希望这篇分享能帮你少踩一些我当年踩过的坑。