上篇我们聊到,Linux的命令行不是一张命令清单,而是一场持续的对话——你敲下一个指令,系统还你一片天地。很多人把“玩Linux”误解成背参数、记快捷键,但我更愿意把它比作学骑车:一开始你盯着脚踏板和刹车,后来你根本不会去想每个动作,却能稳稳地在风里穿行。这篇下篇,我想接着上篇的话题往下走,不讲太多基础语法,而是聊聊管道、脚本、权限、进程和终端定制——这些东西组合在一起,才构成了Linux真正的自由感。适合给自己配环境、跑服务、写脚本的人参考;如果你刚接触Linux,上篇带你逛完了文件和目录,这一篇就带你把这些碎片真正串起来。
1. 管道与重定向:把零散的指令串成一首完整的诗
单独看ls、cat、grep,每一个都只是“点一枪”。真正让Linux从"工具"升级成"天地"的,是组合。而组合的第一步,就是管道和重定向。我的理解是:管道是用来连接人和机器的语法,本质上就是"前一条指令的输出,直接喂给下一条指令当输入",中间不需要落盘,不需要手工复制,一气呵成。
1.1 管道:像流水线一样把命令接起来
先说最基本的形式:
cat /var/log/syslog | grep "error" | head -20这条命令的意思是:读取系统日志 → 只保留包含error的行 → 只看前20行。三个原本独立的命令,通过|串成了一条流水线。你可能说,grep自己就能读文件,为什么要cat再grep?因为管道真正厉害的地方在于"任意组合":你可以ps aux | grep nginx,也可以history | sort | uniq -c | sort -rn | head,甚至可以把一个命令的输出通过管道送给任何"愿意从标准输入里读数据"的程序。
我经常给新手打个比方:管道就像工厂流水线,每个工人只做一件事,做完就传给下一个人。Linux的基本哲学是"每个程序都只做好一件事,并把结果留给别人用",所以几乎所有的命令都约定俗成地遵守"从标准输入读、往标准输出写"的规则。这就是自由协作的模型。
另外有个细节值得注意:管道里的命令是同时运行的,数据像水流一样持续流过,而不是等前一个命令全部跑完再开工。这意味着你可以用tail -f配合grep实时过滤日志:
tail -f /var/log/syslog | grep "nginx"看到一条报错,屏幕上立刻多一行,不需要重启任何东西,也不需要写脚本定时去检查。这种"实时流动"带来的快感,是文件操作方式永远给不了的。
1.2 重定向:把杂音引开,只留下你想看的
如果说管道是"把输出传给另一个程序",重定向就是"把输出写到文件里"。Linux默认给每个进程准备了三个数据流:标准输入(0)、标准输出(1)、标准错误(2)。很多人只认识>,结果遇到报错信息没有被重定向,就一脸懵。
举个例子:
# 覆盖写入文件 ls -l /etc > etc_list.txt # 追加内容 ls -l /var >> etc_list.txt # 只想看错误信息 find / -name "*.conf" 2>/dev/null | head -20 # 把正常输出和错误一起存进一个文件 command > all.log 2>&1这里最容易踩的坑有两个。一是把>和>>搞混:>会直接覆盖整个文件,如果你本来是想追加,一不留神就把之前的记录清空了。我有个朋友调试配置时执行了echo "something" > config.ini,结果整个配置面板直接变成了一行字,当场傻眼。所以拿不准的时候,先cp config.ini config.ini.bak,再用>>。二是漏掉2>&1或2>/dev/null:如果不重定向错误,脚本在cron里一旦遇到权限问题,只会留下莫名其妙的空白日志。
/dev/null是Linux里的黑洞,所有写进去的东西都会消失。它不是"没有意义"的存在,恰恰相反,它的意义是"让你只关注真正想关注的信息"。排查问题的时候,把无关干扰引到黑洞里,是一种必要的克制。
1.3 tee与xargs:在传递中留住瞬间,在组合中解决分歧
管道很爽,但有个尴尬:数据流过去之后,你就看不到它了。想"边看边存",用tee:
ps aux | tee process.log | grep nginxtee像流水线上安装了一个分叉的监视器,把当前数据原样存进process.log,同时继续往下传。所以一边能存日志,一边能实时看到经过筛选的内容,非常适合调试。
xargs则是解决另一类问题:有些命令不读标准输入,只接受命令参数。最典型的例子是wc -l,你只find . -name "*.txt" | wc -l得到的是一个文件数量,而不是每个文件的行数。要统计所有txt文件各自多少行,得这样:
find . -name "*.txt" -print0 | xargs -0 wc -l-print0配合-0是为了防止文件名带空格时被拆错。这是xargs最常见的坑:默认按空白字符分割,遇到带空格的文件就会断成两截,轻则统计错误,重则误删文件。稳妥的做法要么加-0,要么用find ... -exec代替。我不想把每个人都训练成xargs爱好者,但至少要知道:不带参数的xargs,默认是"等空格,不等文件"。
2. 脚本与定时任务:把自由从一次次的“手动敲击”里赎回来
自由最大的敌人是重复。当同一组三条命令你每天都要敲一遍时,就该写脚本了。脚本不是高深莫测的魔法,它就是把命令一行行写进文件,然后让机器替你去执行。
2.1 第一个脚本:从“记住命令”到“让机器记住你”
一个最朴素的备份脚本长这样:
#!/bin/bash BACKUP_DIR=~/backups DATE=$(date +%Y%m%d) mkdir -p "$BACKUP_DIR" tar -czf "$BACKUP_DIR/site_$DATE.tar.gz" /var/www/html find "$BACKUP_DIR" -name "site_*.tar.gz" -mtime +7 -delete echo "backup done: $BACKUP_DIR/site_$DATE.tar.gz"第一行#!/bin/bash非常重要,它告诉系统"用bash来读这个脚本"。如果缺了它,你执行./backup.sh会报错,或者被当成别的解释器处理。$(date +%Y%m%d)把当前日期嵌入文件名,这样每天生成一个不同文件;-mtime +7 -delete顺手把7天前的旧备份清理掉。保存后,先chmod +x backup.sh给执行权限,然后./backup.sh就能跑了。
脚本里的变量、条件、循环都没有太大玄机:
for i in {1..5}; do echo "hello $i" done if [ -f /etc/nginx/nginx.conf ]; then echo "nginx config exists" fi我觉得对新手来说,最需要转变的心态是:脚本不是在“编程”,而是在“把一次性的事情变得可以重复”。你不需要先学会循环语法再写脚本,你完全可以把三行命令先存进文件、手动执行一百次,等某天发现"这行命令总要在另一个目录里执行"时,再补一个cd或变量进去。
2.2 crontab:把重复的事交给未来的某个时刻
脚本写好了,仍然需要人肉去执行。真正让它跑起来的,是crontab。执行crontab -e,添加一行:
30 2 * * * /home/user/backup.sh >> /home/user/backup.log 2>&1五段数字对应:分钟、小时、日期、月份、星期。
| 字段 | 范围 | 含义 |
|---|---|---|
| 分钟 | 0-59 | 每小时的第几分执行 |
| 小时 | 0-23 | 每天第几小时执行 |
| 日期 | 1-31 | 每月的第几天执行 |
| 月份 | 1-12 | 每年的第几月执行 |
| 星期 | 0-6 | 每周第几天(0为周日) |
30 2 * * *就是每天凌晨2点30分。注意cron的环境变量极其精简,它不会加载你.bashrc里的alias和PATH。所以脚本里的命令最好写绝对路径(比如/usr/bin/tar),或者在脚本开头统一export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin。我见过很多次"脚本手动跑没问题,一进cron就报错"的案例,十有八九是PATH问题。
另一个坑是日志。cron默认会把脚本的所有输出通过邮件发给本地用户,如果系统没有配置邮件服务,邮件通知会堆积成隐患。所以给脚本的>> log 2>&1不是可选项,而是必需品。
2.3 alias:给命令起一个只有你懂的小名
如果说脚本是"批处理",那alias就是"快捷键"。在~/.bashrc里写上一堆别名,重启终端或source ~/.bashrc,日常工作量立刻能降一个量级:
alias ll='ls -alF' alias ..='cd ..' alias gp='git pull' alias gs='git status' alias dps='docker ps' alias dp='docker compose' alias py='python3' alias note='vim ~/notes/linux.md'我的建议是:任何"你一周内至少用五次"的长命令,都值得一个alias。比如装完Docker之后,docker ps再配合fzf选容器ID实在太日常了,一个dps就能省一截输入。Python也类似,Ubuntu上python3明明就是那个python,但总有人因为习惯问题来回敲错,一个小小alias就没人再抱怨这事了。
安全一点的alias是alias rm='rm -i',每次删除前都问一下,刚开始觉得烦,但经历过误删之后就会觉得香。真正的自由,不是敲得更多,而是想要什么,顺手就能拿到。
3. 权限与sudo:自由不是为所欲为,而是知道自己被信任
浪漫的另一面是尊重边界。在Linux里,权限不是网管为了限制你而设的枷锁,它是一套让每个人都安全生活的契约。我第一次认认真真理解chmod三个数字时,突然意识到:这正是这台机器信任我的方式。
3.1 chmod数字背后的意义:rwx其实是三种协商
先看一个常见的输出:
-rw-r--r-- 1 root root 1234 Jan 12 10:00 id_rsa第一段的10个字符里,第一个是文件类型(-普通文件、d目录、l符号链接),后面9个字符每3个一组:属主、属组、其他人。每个组里按顺序是读(r)、写(w)、执行(x)。所以rw-r--r--意思是"属主可读写,属组可读,其他人可读"。
数值化的原因也很简单:r=4、w=2、x=1,把三段权限分别加起来,就是三位数字。4+2+1=7,4+0+0=4,所以:
chmod 755 deploy.sh # 属主7(可读写执行),属组5(读+执行),其他5 chmod 640 id_rsa # 属主6(读写),属组4(只读),其他无 chmod 600 .ssh/id_rsa # 只有属主能读写,其他一律拒绝这里我非常想多说一句:不要随手chmod 777。777等于把门敞开,任何人都能改掉你的文件。尤其对于密钥、脚本、配置文件,给到600或640通常已经足够。别用"图省事"作为理由——一个自由的环境,依赖的是每个人都守住自己的边界。
目录权限也需要chmod -R慎重使用,递归修改很容易把不该改的也改了。通常我更推荐先ls -l看清楚,再针对具体文件改,而不是大范围无差别修改。
3.2 root、普通用户与sudo:一条需要克制使用的特权通道
su和sudo是两个经常被混为一谈的命令。su是"切换用户",通常切到root;sudo是"借用特权身份执行这一条命令",默认执行者仍是当前用户,身份是root。比如:
sudo apt update sudo systemctl restart nginx sudo -i # 进入root的shell,非必要别用为什么日常不建议一直待在root里?因为root操作没有任何护栏。rm -rf 在普通用户下最糟糕也就删掉自己目录里的文件;在root下,一个手滑可以瞬间毁掉整个系统。用sudo,至少每次都有记录,出问题能追溯是哪个用户哪个时间敲了哪条命令。这个"可追溯性",本身就是自由的一部分。
我常对学员说:真正的自由是"知道我有权限去做,但仍然选择谨慎地做"。sudo不是麻烦,它是给疯马配的缰绳,不是限制,而是让你跑得更远。
3.3 umask:从创建文件那一刻,边界就写好了
每次新建文件、新建目录,系统默认给予的权限不是固定的,它由umask决定。执行umask查看当前值,通常输出0022。这个值表示"新建文件/目录时要屏蔽掉的权限位":默认文件权限是666,目录是777,减去umask,所以新建文件通常是644,目录是755。
如果你希望新文件别人完全看不到,就umask 077,之后新建的文件默认600、目录700。我在跑Web服务的时候,经常把上传目录的umask设成027,这样同组合作的人可读,但其他人完全不可见。
umask给我最大的启发是:边界不是事后再修的,而是从文件诞生那一刻就写好在基因里的。Linux的浪漫,始终都在这些"默认动作"里。
4. 进程、守护与网络:浪漫也要有“不轻易死掉”的韧劲
有些程序需要一直跑着,即使没有人盯着它。这是个关于"韧劲"的话题,也是Linux里最难被新人理解的一块。你会发现自己变得像一个花匠,不光要会种花,还要知道什么时候浇水、什么时候修剪、什么情况下可以安心睡一觉。
4.1 ps、top与kill:看清楚谁在跑,再决定让谁谢幕
查进程最常用的组合是ps aux,a显示所有用户、u显示详细信息、x显示没有控制终端的进程。配合grep可以快速定位:
ps aux | grep nginx # 会看到一列进程,以及各自PID top # 动态查看CPU和内存占用找到PID之后,就可以kill 1234,这是发送SIGTERM信号,属于"礼貌地请进程退出"。它给了程序保存状态、清理资源的时间。如果它不响应,再考虑kill -9 1234,这是SIGKILL,强制杀死,没有任何协商余地。
我特别想强调:kill -9是最后手段。它就像直接拔电源,再简单粗暴不过,但数据库、写入中的服务被这么一杀,很可能留下损坏文件。我见过有人杀一个卡住的Python训练脚本,不管三七二十一kill -9,第二天才发现模型保存一半,整个文件都废了。正确做法是:先给进程一个缓冲时间,kill,等30秒实在没动静,再上kill -9。
4.2 nohup、systemd与journalctl:让服务在你看不见的地方继续活着
如果只是临时让一个脚本在退出终端后继续跑,用nohup:
nohup ./run.sh > run.log 2>&1 &&是放到后台,nohup是忽略挂断信号(HUP),这样即使关闭SSH,脚本也不会跟着退出。它的定位是"临时托付"。
更正式的做法是写一个systemd服务,让系统来管理它的启停、崩溃重启和开机自启。先建一个服务文件/etc/systemd/system/myapp.service:
[Unit] Description=My App After=network.target [Service] ExecStart=/home/user/app/app.py Restart=on-failure User=user [Install] WantedBy=multi-user.target然后:
systemctl daemon-reload systemctl start myapp systemctl enable myapp systemctl status myappRestart=on-failure的意思是"进程崩溃时自动拉起来",只要守护进程还在,服务就不会轻易“死掉”。查看运行日志用journalctl -u myapp -f,比大海捞针一样翻文件高效得多。
把nohup比喻成"临时找熟人托管",systemd则更像"正式领养"。守护进程是真的在房间里守夜的那位——平时你看不见他,但半夜服务意外退出,他立刻就能把人拉回来。
4.3 ss与lsof:管好那些悄悄开着的端口
聊到网络就绕不开端口排查。最常用的场景是:启动应用时报"端口被占用"。这时候ss最直接:
ss -tlnp | grep 8080 # 输出会看到 LISTEN 0 ... *:8080 users:(("java",pid=9527,fd=380))看到PID之后,就可以决定这个进程是该保留还是该结束。老一点的系统没有ss,用netstat -tlnp替代,但ss更快更直观。如果你想知道某个端口被哪个进程占用,也可以用lsof -i :8080。
这里依然要提一句:如果是数据库、缓存这类进程占据了端口,别仗着"端口被占用了"就急着kill -9。先看看那个进程是什么、为什么还在,也许它只是在等一个正在写了一半的数据。礼貌一点,先用kill,再观察。
5. 给终端写一封情书:定制一套顺手且赏心悦目的命令行环境
一个人怎么用终端,基本能看出他对待生活的态度。有人连终端提示符都懒得改,一直黑底白字;有人花半小时配一个赏心悦目的提示符,每条路径都清清楚楚。我倒不觉得后者更高级,但适合自己的终端,确实能省下大量注意力。
5.1 PS1与PROMPT_COMMAND:让提示符为你说话
默认的提示符通常是user@host:~$,虽然够用,但信息密度太低。用PS1可以自定义:
export PS1='\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '这样用户名和主机名是绿色,当前路径是蓝色,后面的$是普通颜色。\u是用户名、\h是主机名、\w是当前路径、\$在普通用户下显示$、root下显示#。颜色码\[\033[01;32m\]看起来吓人,其实只需要记住"开始一个颜色用\[\033[01;32m\],恢复默认用\[\033[00m\]"就够了。
如果你想在提示符里显示其他信息,比如git分支,可以用PROMPT_COMMAND:它在每次显示提示符之前执行一段命令。我一般在仓库目录里能看到当前分支,省去反复敲git branch的麻烦。
写入~/.bashrc之后source ~/.bashrc就永久生效。这个小改动,会让你每次看到终端都更舒服一点,千万别小看这种"舒适感"对长期效率的影响。
5.2 history与快捷键:让回忆变快的那些小事
敲过一百遍的命令,如果每次都要重新打一遍,那就太亏了。Linux里history记录了你输入过的所有命令,配合几个快捷键能大幅提速:
!!:重复上一条命令(比如sudo !!,上一条权限不够,加sudo再跑一次)!$:上一条命令的最后一个参数Ctrl+R:反向搜索历史,输入几个字母,整条命令就回来了
举个例子:
mkdir /tmp/long_dir cd !$ # 等价于 cd /tmp/long_dir设置历史忽略重复项,能让你搜索时不那么吵闹:
export HISTCONTROL=ignoredups:erasedups export HISTSIZE=10000此外还可以把会话共享打开,多个终端之间共享历史。不过这是个人偏好,我自己更喜欢按终端隔离,避免两个项目的历史互相污染。
5.3 顺手的小工具组合:grep高亮、df/du、tmux与那些能节省半天的小东西
终端体验的下一步,是给常用命令加一点点“滤镜”。
alias grep='grep --color=auto'这一行让grep匹配到的关键词有颜色,找日志时高亮显示关键字,效率立刻不一样。再看看磁盘:
df -h # 人类可读的磁盘占用 du -sh * # 当前目录下每个子目录占了多少空间如果有个长任务需要挂机,又担心SSH断了会话就丢,用tmux。新建会话tmux new -s work,中途断开,重新连上服务器后tmux attach -t work就能回到原来的会话里。这种感觉就像把工作现场摆在桌上,不管中途发生了什么,回去继续干就是了。
再往上一点,装个fzf,配合Ctrl+R就能变成交互式模糊搜索,敲几个字符,整个历史里相关命令都浮上来。装完Docker、Python之后,再给常用命令配好别名,这组"小东西"组合起来,日常效率提升其实是肉眼可见的。
6. 当浪漫遭遇意外:排错思路才是真正的长期主义
写了几年脚本,我最大的变化不是记住的命令更多,而是面对报错不再心慌。Linux的报错信息有时候很冷,但它从来不是在骂你,而是在指路——关键是你要懂得往哪看。
6.1 从报错信息开始:读日志比问人更有用
出了问题,第一步永远是看日志。系统服务用systemd管理时,journalctl是一站式入口:
journalctl -xe # 查看最近的错误和相关服务日志 journalctl -u nginx -n 50 --no-pager # 只看nginx最近50行 journalctl -u myapp -f # 实时跟踪某个服务的日志不是systemd再管理的应用,就去看它自己的应用日志,通常都在/var/log/下。网络问题,可以看dmesg输出的内核日志。实时跟踪一个文件,用tail -f。
有个很常见的年轻人毛病:报错一弹出来就开始找同事、发截图。其实大多数报错的第一行里,已经写清楚了"哪个命令、哪个文件、哪个行号"。最稳的排错路径是:先自己读日志,再自己复现,最后实在没思路才去提问。提问的时候带着日志原文和操作步骤,人家也愿意帮你。
6.2 隔离变量:二分法定位问题的通用套路
排错的核心不是碰运气,是"隔离变量"。大概流程是:
- 确认问题范围:是我这台机器的问题,还是所有机器都这样?是配置问题,还是依赖问题?
- 直接运行命令,看它真正的报错是什么,不要只看包装后的提示。
- 如果脚本不执行,先看文件权限:
ls -l script.sh,没有执行权限就chmod +x。 - 如果权限没问题,看解释器:
which bash、检查脚本第一行是否正常。 - 用
bash -x script.sh追踪每一步执行,哪一步报错,立刻就能看到。
举一个真实场景:./deploy.sh报Permission denied。这时最不应该做的就是chmod 777 deploy.sh。先ls -l deploy.sh,看到权限确实是-rw-------,属主是你自己,那就chmod 755 deploy.sh;如果属主是root,就要考虑是不是该sudo chown调整归属。还有一种是Windows上编辑过的脚本,在Linux里会报bad interpreter,因为文件带Windows换行符或CRLF,这时候用dos2unix处理一下就行。
再举cron的例子:任务到点了没跑。排查三步:crontab -l看任务是否真的保存了;查/var/log/syslog里crontab有没有执行行;最后手动执行脚本bash -x /home/user/backup.sh,确认脚本本身没有在新环境里缺少依赖。大多数时候,问题就藏在"环境变量没设置"或"脚本里用了alias没有生效"这种细枝末节里。
6.3 复盘笔记:把你踩过的坑变成长期的自由
最后,我强烈建议每个人都要有一套自己的"排错笔记"。我不指望记住所有命令,但我会在~/notes里建一个linux.md,把每一次踩过的坑、搜索到的解法、排错链路都记下来。配合alias:
alias note='vim ~/notes/linux.md'下次遇到同样的问题,直接note打开,搜索一下关键字,几分钟就能解决。这个方法的价值不是让你显得勤奋,而是让你的"自由"是可持续的——你不需要每次从零开始,你的经验会像滚雪球一样累积。
写在最后,分享一点我个人的体会。写了这么多年脚本,我越来越觉得,Linux给人的自由不是漫无边际的撒野,而是在理解规则之后的从容。每一条指令,都是你和系统之间的一次对话;当你学会了管道、脚本、权限和排错,这个对话会变得越来越顺畅,你甚至能在深夜盯着一条实时滚动的日志时,感到一种奇异的平静。
上篇我们说命令是入口,下篇我想说入口后面不是迷宫,而是一张可以按照自己习惯随时重新布置的桌子。去跑一遍ps aux | grep "你的用户名",看看系统里都有哪些进程正在为你工作;去给最常用的长命令配一个alias;再去写一个属于自己的备份脚本。风吹字符起,也不过是敲对了那个回车而已。