Ubuntu 命令行实战指南

这不是一本教科书式的命令手册,而是一份面向真实场景的实战指南。当你需要部署一个服务、排查一个问题、管理一台服务器时,你会在这里找到答案。

Ubuntu 命令行实战指南
Photo by Gabriel Heinzer / Unsplash

一、命令行效率神器

1.1 历史命令快速搜索

Ctrl+R 就是其中之一:

# Ctrl+R: 反向搜索历史命令
# 输入关键词,自动匹配你用过的命令
# 再按 Ctrl+R 继续向前搜索
# Enter 执行,Ctrl+C 取消

# 查看所有历史
history

# 执行历史中的第123条命令
!123

# 执行上一条命令
!!

# 执行最近一条以 docker 开头的命令
!docker

1.2 其他效率技巧

# Ctrl+A: 跳到行首
# Ctrl+E: 跳到行末
# Ctrl+U: 删除光标前的所有内容
# Ctrl+K: 删除光标后的所有内容
# Ctrl+W: 删除光标前的一个单词
# Ctrl+L: 清屏(等同于 clear)

# Alt+.: 粘贴上一条命令的最后一个参数
# 场景: 你刚 ls /var/log/nginx/,现在想 cd 过去
cd Alt+.  # 自动补全为 cd /var/log/nginx/

二、服务管理实战

2.1 场景:部署一个 Nginx 服务

第一步:安装

sudo apt update
sudo apt install nginx

第二步:检查服务状态

# 查看服务状态(最常用)
sudo systemctl status nginx

# 查看是否正在运行
sudo systemctl is-active nginx

# 查看是否开机自启
sudo systemctl is-enabled nginx

第三步:管理服务

# 启动服务
sudo systemctl start nginx

# 停止服务
sudo systemctl stop nginx

# 重启服务(配置改了后用这个)
sudo systemctl restart nginx

# 重新加载配置(不中断服务,推荐)
sudo systemctl reload nginx

# 设置开机自启
sudo systemctl enable nginx

# 取消开机自启
sudo systemctl disable nginx

# 一步到位:启用并立即启动
sudo systemctl enable --now nginx

2.2 场景:创建自己的服务

假设你写了一个 Python 程序 /home/user/myapp/app.py,想让它作为服务运行:

创建服务文件

sudo nano /etc/systemd/system/myapp.service

服务文件内容

[Unit]
Description=My Python Application
After=network.target

[Service]
Type=simple
User=user
WorkingDirectory=/home/user/myapp
ExecStart=/usr/bin/python3 /home/user/myapp/app.py
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target

关键配置说明

  • After=network.target: 网络就绪后再启动
  • Type=simple: 最常用的类型,程序前台运行
  • Restart=always: 挂了自动重启
  • RestartSec=10: 重启前等待 10 秒
  • WantedBy=multi-user.target: 开机自启

启用服务

# 重新加载 systemd 配置
sudo systemctl daemon-reload

# 启动并设置自启
sudo systemctl enable --now myapp

# 查看状态
sudo systemctl status myapp

2.3 场景:找出占用端口的进程

使用 lsof(最常用)

# 查看 8080 端口被谁占用
sudo lsof -i :8080

# 输出示例:
# COMMAND   PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
# nginx    1234 root    6u  IPv4  12345      0t0  TCP *:8080 (LISTEN)

# 只显示 PID
sudo lsof -ti :8080

# 查看某个进程打开了哪些文件
sudo lsof -p 1234

# 查看某个用户打开的所有文件
sudo lsof -u username

# 查看某个目录下被打开的文件
sudo lsof +D /var/log

# 查看某个文件被哪些进程打开
sudo lsof /var/log/nginx/access.log

# 查看所有网络连接
sudo lsof -i

# 查看 TCP 连接
sudo lsof -i tcp

# 查看 UDP 连接
sudo lsof -i udp

# 查看某个 IP 的连接
sudo lsof -i @192.168.1.1

# 持续监控某个端口(每 2 秒刷新)
watch -n 2 'sudo lsof -i :8080'

使用 netstat

# 查看所有监听的端口
sudo netstat -tlnp
# -t: TCP
# -l: 监听中的
# -n: 显示数字地址
# -p: 显示进程信息

# 查看所有连接(包括已建立的)
sudo netstat -anp

# 查看 8080 端口
sudo netstat -tlnp | grep 8080

# 统计各状态的连接数
netstat -an | awk '/^tcp/ {print $6}' | sort | uniq -c

使用 ss(更现代,推荐)

# 查看所有监听的 TCP 端口
sudo ss -tlnp

# 查看 8080 端口
sudo ss -tlnp | grep 8080

# 查看所有已建立的连接
sudo ss -tnp

# 查看连接统计
ss -s

# 查看某个进程的网络连接
sudo ss -tp | grep nginx

一步杀掉占用端口的进程

# 找到 PID 并杀掉
sudo kill $(sudo lsof -ti :8080)

# 强制杀掉
sudo kill -9 $(sudo lsof -ti :8080)

# 或者用 fuser
sudo fuser -k 8080/tcp

2.4 场景:查看服务的实时日志

systemd 服务日志(推荐)

# 查看服务日志(最近的)
sudo journalctl -u nginx

# 实时滚动查看(最常用)
sudo journalctl -u nginx -f

# 查看最近 100 行
sudo journalctl -u nginx -n 100

# 从最后开始看,并实时更新
sudo journalctl -u nginx -f -n 50

# 查看今天的日志
sudo journalctl -u nginx --since today

# 查看最近 1 小时
sudo journalctl -u nginx --since "1 hour ago"

# 查看某个时间段
sudo journalctl -u nginx --since "2024-10-20 10:00" --until "2024-10-20 11:00"

# 显示详细信息(包括完整输出)
sudo journalctl -u nginx -o verbose

# 只看错误级别的日志
sudo journalctl -u nginx -p err

# 查看多个服务的日志
sudo journalctl -u nginx -u mysql

查看应用自己的日志文件

# 实时查看日志末尾(最常用)
tail -f /var/log/nginx/access.log

# 查看最后 50 行
tail -n 50 /var/log/nginx/error.log

# 查看前 20 行
head -n 20 /var/log/nginx/access.log

# 实时查看多个日志
tail -f /var/log/nginx/access.log /var/log/nginx/error.log

# 分页查看日志(可上下翻页)
less /var/log/nginx/access.log
# 空格:下一页
# b:上一页
# /:搜索
# q:退出

查看 nohup 后台进程的日志

# 如果用了 nohup python3 app.py > app.log 2>&1 &
tail -f app.log

# 如果没指定输出文件,默认在 nohup.out
tail -f nohup.out

2.5 场景:在日志中查找特定内容

使用 grep 搜索日志

# 在日志中搜索包含 "error" 的行
grep "error" /var/log/nginx/error.log

# 忽略大小写
grep -i "error" /var/log/nginx/error.log

# 显示行号
grep -n "error" /var/log/nginx/error.log

# 显示匹配行的前后 3 行(看上下文)
grep -C 3 "error" /var/log/nginx/error.log

# 只显示前 5 行
grep -B 5 "error" /var/log/nginx/error.log

# 只显示后 5 行
grep -A 5 "error" /var/log/nginx/error.log

# 统计匹配次数
grep -c "error" /var/log/nginx/error.log

# 显示不包含某个词的行(反向搜索)
grep -v "debug" /var/log/app.log

# 使用正则表达式
grep -E "error|warning|critical" /var/log/nginx/error.log

# 递归搜索目录下所有文件
grep -r "error" /var/log/nginx/

# 只显示文件名,不显示内容
grep -l "error" /var/log/nginx/*

实时搜索日志

# 实时查看包含 "error" 的日志
tail -f /var/log/nginx/error.log | grep "error"

# 高亮显示匹配的内容
tail -f /var/log/nginx/error.log | grep --color "error"

# 实时查看 systemd 日志中的错误
sudo journalctl -u nginx -f | grep -i error

组合过滤(管道操作)

# 查找包含 "error" 但不包含 "debug" 的行
grep "error" /var/log/app.log | grep -v "debug"

# 统计每个 IP 的访问次数
cat /var/log/nginx/access.log | awk '{print $1}' | sort | uniq -c | sort -rn

# 查找状态码为 500 的请求
grep " 500 " /var/log/nginx/access.log

# 查看最近 1 小时的错误,并统计数量
sudo journalctl -u nginx --since "1 hour ago" | grep -i error | wc -l

使用 awk 提取特定字段

# 提取 Nginx access.log 中的 IP 地址(第一列)
awk '{print $1}' /var/log/nginx/access.log

# 提取请求的 URL(第七列)
awk '{print $7}' /var/log/nginx/access.log

# 提取状态码(第九列)
awk '{print $9}' /var/log/nginx/access.log

# 条件过滤:只显示状态码为 500 的行
awk '$9 == 500' /var/log/nginx/access.log

# 统计各个状态码的数量
awk '{print $9}' /var/log/nginx/access.log | sort | uniq -c | sort -rn

使用 sed 过滤时间范围

# 查看某个时间段的日志
sed -n '/2024-10-21 10:00/,/2024-10-21 11:00/p' /var/log/app.log

# 删除包含某个词的行后再查看
sed '/debug/d' /var/log/app.log

三、文件编辑与配置

3.1 文本编辑器选择

# nano: 最简单,适合新手
nano filename.txt
# Ctrl+O 保存, Ctrl+X 退出

# vim: 功能强大,有学习曲线
vim filename.txt
# 按 i 进入编辑模式
# 按 Esc 退出编辑模式
# 输入 :wq 保存并退出
# 输入 :q! 不保存退出

# 如果只是查看文件
cat filename.txt
less filename.txt  # 可以翻页,q 退出

3.2 场景:编辑 Nginx 配置

# 编辑主配置
sudo nano /etc/nginx/nginx.conf

# 编辑站点配置
sudo nano /etc/nginx/sites-available/default

# 测试配置是否有语法错误(重要!)
sudo nginx -t

# 配置正确后重新加载
sudo systemctl reload nginx

3.3 场景:修改环境变量

# 临时修改(关闭终端就失效)
export PATH=$PATH:/opt/myapp/bin

# 永久修改(当前用户)
nano ~/.bashrc
# 在文件末尾添加:
# export PATH=$PATH:/opt/myapp/bin

# 使配置生效
source ~/.bashrc

# 或者重新登录

四、文件与目录操作

4.1 查找文件

# 按名称查找
find /var/log -name "*.log"

# 查找最近 7 天修改的文件
find /var/log -mtime -7

# 查找大于 100M 的文件
find /var/log -size +100M

# 在文件内容中搜索(递归搜索目录)
grep -r "error" /var/log/nginx/

# 忽略大小写
grep -ri "error" /var/log/nginx/

# 显示行号
grep -rn "error" /var/log/nginx/

4.2 磁盘空间管理

# 查看磁盘使用情况
df -h

# 查看当前目录下各子目录的大小
du -h --max-depth=1

# 找出最大的 10 个文件
du -ah /var/log | sort -rh | head -10

# 查看某个目录的总大小
du -sh /var/log/nginx/

4.3 文件权限与所有者

# 查看详细信息
ls -lh

# 修改所有者
sudo chown user:group filename

# 递归修改目录
sudo chown -R user:group /var/www/html

# 修改权限(755: 所有者全权限,其他人读+执行)
chmod 755 script.sh

# 让脚本可执行
chmod +x script.sh

# 常用权限:
# 644: 文件(所有者读写,其他人只读)
# 755: 目录或可执行文件
# 600: 私密文件(如 SSH 密钥)

五、进程管理

5.1 查看进程

# 查看所有进程
ps aux

# 查看特定进程(比如 nginx)
ps aux | grep nginx

# 实时查看进程(类似 Windows 任务管理器)
top
# 或更友好的
htop  # 需要先安装: sudo apt install htop

# 按内存排序查看前 10 个进程
ps aux --sort=-%mem | head -10

# 按 CPU 排序
ps aux --sort=-%cpu | head -10

5.2 后台运行与进程管理

场景:需要运行一个长期任务,退出 SSH 后还要继续运行

# 方法一:使用 nohup(最常用)
nohup python3 app.py > app.log 2>&1 &
# nohup: 忽略挂断信号
# > app.log: 标准输出重定向到文件
# 2>&1: 错误输出也重定向到同一文件
# &: 后台运行

# 查看 nohup 进程输出
tail -f app.log

# 方法二:使用 screen(可以随时重新连接)
screen -S myapp
python3 app.py
# 按 Ctrl+A 然后按 D 断开(进程继续运行)

# 重新连接到 screen
screen -r myapp

# 查看所有 screen 会话
screen -ls

# 方法三:使用 tmux(更现代的 screen)
tmux new -s myapp
python3 app.py
# 按 Ctrl+B 然后按 D 断开

# 重新连接
tmux attach -t myapp

# 查看 tmux 会话
tmux ls

查看后台任务

# 查看当前终端的后台任务
jobs

# 查看后台任务的 PID
jobs -l

# 把后台任务调到前台
fg %1  # 1 是任务编号

# 把前台任务暂停并放到后台
Ctrl+Z  # 先暂停
bg      # 再后台运行

# 杀掉后台任务
kill %1

六、网络调试与诊断

6.1 测试连接

# 测试网络连通性
ping google.com
ping -c 4 google.com  # 只 ping 4 次

# 测试端口是否开放
telnet example.com 80

# 或用 nc(netcat,更强大)
nc -zv example.com 80
# -z: 扫描模式,不发送数据
# -v: 详细输出

# 扫描端口范围
nc -zv example.com 80-100

# 监听端口(测试用)
nc -l 8080  # 在一个终端监听
nc localhost 8080  # 在另一个终端连接

# 测试 UDP 端口
nc -zvu example.com 53

6.2 HTTP 请求测试

# 基本 GET 请求
curl http://localhost:8080

# 查看完整的请求和响应头
curl -v http://localhost:8080

# 只看响应头
curl -I http://localhost:8080

# POST 请求
curl -X POST -d "key=value" http://localhost:8080/api

# POST JSON 数据
curl -X POST \
  -H "Content-Type: application/json" \
  -d '{"name":"test","age":25}' \
  http://localhost:8080/api

# 带认证的请求
curl -u username:password http://localhost:8080/api

# 带 Cookie
curl -b "session=abc123" http://localhost:8080

# 保存 Cookie
curl -c cookies.txt http://localhost:8080/login

# 使用保存的 Cookie
curl -b cookies.txt http://localhost:8080/dashboard

# 下载文件
curl -O https://example.com/file.zip

# 指定保存的文件名
curl -o myfile.zip https://example.com/file.zip

# 断点续传
curl -C - -O https://example.com/largefile.zip

# 限速下载(100KB/s)
curl --limit-rate 100K -O https://example.com/file.zip

# 跟随重定向
curl -L http://example.com

# 测试接口响应时间
curl -w "@-" -o /dev/null -s http://localhost:8080 <<'EOF'
    time_namelookup:  %{time_namelookup}\n
       time_connect:  %{time_connect}\n
    time_appconnect:  %{time_appconnect}\n
      time_redirect:  %{time_redirect}\n
   time_pretransfer:  %{time_pretransfer}\n
 time_starttransfer:  %{time_starttransfer}\n
                    ----------\n
         time_total:  %{time_total}\n
EOF

6.3 DNS 查询

# 查询域名的 IP
nslookup google.com

# 更详细的 DNS 查询
dig google.com

# 只显示 IP 地址
dig +short google.com

# 查询 MX 记录(邮件服务器)
dig google.com MX

# 查询 DNS 记录类型
dig google.com ANY

# 指定 DNS 服务器查询
dig @8.8.8.8 google.com

# 反向查询(IP 查域名)
dig -x 8.8.8.8

# 使用 host 命令
host google.com

6.4 路由追踪

# 追踪到目标的网络路径
traceroute google.com

# 或用 tracepath(不需要 root)
tracepath google.com

# 使用 mtr(更好的追踪工具)
mtr google.com
# 实时显示每一跳的延迟和丢包率

6.5 网络连接查看

# 查看所有网络连接
ss -tunapr

# 查看建立的连接
ss -tn state established

# 查看监听的端口
ss -tln

# 查看哪些进程连接到远程服务器
ss -tnp | grep 443

# 统计连接状态
ss -tan | awk '{print $1}' | sort | uniq -c

# 查看网络接口
ip addr show

# 查看路由表
ip route show

# 查看 ARP 表
ip neigh show

6.6 抓包分析

# 安装 tcpdump
sudo apt install tcpdump

# 抓取所有网卡的包
sudo tcpdump

# 抓取指定网卡
sudo tcpdump -i eth0

# 抓取指定端口
sudo tcpdump port 80

# 抓取指定 IP
sudo tcpdump host 192.168.1.100

# 抓取并保存到文件
sudo tcpdump -i eth0 -w capture.pcap

# 读取抓包文件
sudo tcpdump -r capture.pcap

# 只抓取 HTTP 请求
sudo tcpdump -i eth0 -A 'tcp port 80 and (tcp[((tcp[12:1] & 0xf0) >> 2):4] = 0x47455420)'

# 抓取特定数量的包
sudo tcpdump -c 100

# 显示详细信息
sudo tcpdump -v port 80

6.7 防火墙管理

# 查看防火墙状态
sudo ufw status

# 查看详细规则(带编号)
sudo ufw status numbered

# 允许某个端口
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow 22/tcp  # SSH,小心别把自己锁外面

# 允许端口范围
sudo ufw allow 6000:6010/tcp

# 允许特定 IP 访问
sudo ufw allow from 192.168.1.100

# 允许特定 IP 访问特定端口
sudo ufw allow from 192.168.1.100 to any port 22

# 拒绝连接
sudo ufw deny 23/tcp

# 删除规则(按编号)
sudo ufw delete 3

# 删除规则(按规则内容)
sudo ufw delete allow 80/tcp

# 启用防火墙
sudo ufw enable

# 禁用防火墙
sudo ufw disable

# 重置所有规则
sudo ufw reset

# 查看应用配置文件
sudo ufw app list

# 使用应用配置
sudo ufw allow 'Nginx Full'

6.8 网速测试

# 安装 speedtest
sudo apt install speedtest-cli

# 测试网速
speedtest-cli

# 只测试下载速度
speedtest-cli --no-upload

# 用字节显示(而不是比特)
speedtest-cli --bytes

# 使用 iperf3 测试局域网速度
# 服务器端:
iperf3 -s

# 客户端:
iperf3 -c 192.168.1.100

七、系统监控与性能分析

7.1 资源使用监控

# 查看内存使用
free -h

# 持续监控内存(每 2 秒刷新)
watch -n 2 free -h

# 查看内存详细信息
cat /proc/meminfo

# 查看 CPU 信息
lscpu

# 查看 CPU 使用率
top
# 按 1: 显示每个 CPU 核心
# 按 P: 按 CPU 排序
# 按 M: 按内存排序
# 按 k: 杀掉进程

# 更友好的 top(需安装)
htop
# F2: 设置
# F3: 搜索进程
# F4: 过滤
# F5: 树状显示
# F9: 杀进程
# F10: 退出

# 查看系统负载
uptime
# 显示: 运行时间, 用户数, 1/5/15 分钟平均负载

# 查看负载详情
w

7.2 磁盘 I/O 监控

# 实时查看磁盘 I/O
iostat -x 2
# 每 2 秒刷新一次

# 查看哪些进程在读写磁盘
sudo iotop

# 只显示正在读写的进程
sudo iotop -o

# 查看磁盘读写统计
sudo iotop -a

# 使用 pidstat 查看进程 I/O
pidstat -d 2

7.3 进程详细分析

# 查看进程树
pstree

# 查看某个进程的树
pstree -p 1234

# 查看进程详细信息
ps aux | grep nginx

# 查看进程的线程
ps -T -p 1234

# 查看进程打开的文件描述符数量
lsof -p 1234 | wc -l

# 查看进程的环境变量
cat /proc/1234/environ | tr '\0' '\n'

# 查看进程的命令行参数
cat /proc/1234/cmdline

# 查看进程的内存映射
cat /proc/1234/maps

# 查看进程的资源限制
cat /proc/1234/limits

# 使用 pidstat 监控进程
pidstat -p 1234 2
# 每 2 秒显示进程 1234 的统计

7.4 系统日志查看

# 查看系统日志
sudo tail -f /var/log/syslog

# 查看认证日志(登录记录)
sudo tail -f /var/log/auth.log

# 查看内核日志
dmesg

# 实时查看内核日志
dmesg -w

# 查看内核错误
dmesg --level=err

# 查看最近的启动日志
journalctl -b

# 查看上一次启动的日志
journalctl -b -1

# 查看所有启动记录
journalctl --list-boots

# 查看内核日志
journalctl -k

# 查看某个时间后的所有日志
journalctl --since "2024-10-20 10:00"

# 查看特定优先级的日志
journalctl -p err
# 优先级: emerg, alert, crit, err, warning, notice, info, debug

# 查看磁盘使用情况
journalctl --disk-usage

# 清理旧日志(保留最近 100MB)
sudo journalctl --vacuum-size=100M

# 清理 7 天前的日志
sudo journalctl --vacuum-time=7d

7.5 性能分析工具

# 查看系统调用
strace ls
# 跟踪 ls 命令的系统调用

# 跟踪正在运行的进程
sudo strace -p 1234

# 统计系统调用
strace -c ls

# 查看进程打开的文件
sudo strace -e open,openat ls

# 使用 ltrace 跟踪库函数调用
ltrace ls

# 使用 perf 进行性能分析(需安装)
sudo perf top
# 实时显示 CPU 热点

# 记录性能数据
sudo perf record -a -g sleep 10

# 查看性能报告
sudo perf report

# 查看系统瓶颈
vmstat 2
# 每 2 秒显示一次虚拟内存统计

# 查看 CPU 统计
mpstat 2

7.6 网络流量监控

# 实时监控网络流量
sudo iftop
# 按 t: 切换显示模式
# 按 h: 帮助
# 按 q: 退出

# 按连接显示流量
sudo iftop -P

# 监控特定网卡
sudo iftop -i eth0

# 使用 nethogs 按进程监控流量
sudo nethogs

# 监控特定网卡
sudo nethogs eth0

# 使用 vnstat 统计流量(需先配置)
vnstat
vnstat -d  # 按天统计
vnstat -m  # 按月统计
vnstat -h  # 按小时统计

八、实用技巧合集

8.1 批量操作

# 批量重命名(把所有 .txt 改成 .md)
for file in *.txt; do mv "$file" "${file%.txt}.md"; done

# 批量删除某类文件
find . -name "*.log" -delete

# 批量修改权限
find /var/www -type f -exec chmod 644 {} \;
find /var/www -type d -exec chmod 755 {} \;

8.2 压缩与解压

# 打包并压缩
tar -czf archive.tar.gz /path/to/directory

# 解压
tar -xzf archive.tar.gz

# 只查看压缩包内容,不解压
tar -tzf archive.tar.gz

# zip 格式
zip -r archive.zip /path/to/directory
unzip archive.zip

8.3 定时任务

# 编辑定时任务
crontab -e

# 常用格式:
# * * * * * command
# 分 时 日 月 周

# 示例:每天凌晨 2 点备份
0 2 * * * /home/user/backup.sh

# 每 5 分钟执行一次
*/5 * * * * /home/user/check.sh

# 查看当前的定时任务
crontab -l

8.4 命令行快捷操作

# 快速创建多级目录
mkdir -p /path/to/nested/directory

# 同时创建文件和父目录
mkdir -p /path/to && touch /path/to/file.txt

# 切换到上一个目录
cd -

# 快速备份文件
cp file.conf{,.bak}  # 等同于 cp file.conf file.conf.bak

# 清空文件内容
> file.txt

# 快速生成大文件(测试用)
dd if=/dev/zero of=test.img bs=1M count=100

九、故障排查实战手册

9.1 服务启动失败排查

完整排查流程:

# 第一步: 查看服务状态
sudo systemctl status servicename
# 看输出的错误信息和退出码

# 第二步: 查看详细日志
sudo journalctl -u servicename -n 100 --no-pager
# 看最近 100 行日志,找错误信息

# 第三步: 实时查看启动过程
sudo journalctl -u servicename -f
# 在另一个终端尝试启动服务
sudo systemctl start servicename

# 第四步: 检查配置文件语法
# Nginx:
sudo nginx -t

# Apache:
sudo apache2ctl configtest

# MySQL:
sudo mysqld --validate-config

# 第五步: 检查端口占用
sudo lsof -i :80
sudo ss -tlnp | grep :80

# 第六步: 检查文件权限
ls -l /etc/nginx/nginx.conf
ls -ld /var/www/html

# 第七步: 检查 SELinux/AppArmor
sudo aa-status  # AppArmor
getenforce      # SELinux

# 第八步: 手动运行程序看错误
# 找到服务的 ExecStart 命令
sudo systemctl cat servicename
# 手动执行那个命令

# 第九步: 查看系统日志
sudo tail -n 50 /var/log/syslog | grep servicename

# 第十步: 检查依赖服务
sudo systemctl list-dependencies servicename

常见错误及解决:

# 错误: Address already in use
# 解决: 杀掉占用端口的进程
sudo lsof -ti :80 | xargs sudo kill -9

# 错误: Permission denied
# 解决: 检查文件权限和所有者
sudo chown -R www-data:www-data /var/www/html
sudo chmod -R 755 /var/www/html

# 错误: No such file or directory
# 解决: 创建缺失的目录
sudo mkdir -p /var/log/myapp
sudo chown myuser:myuser /var/log/myapp

# 错误: Failed to start (timeout)
# 解决: 增加启动超时时间
# 编辑服务文件,添加:
# TimeoutStartSec=300

9.2 磁盘空间不足排查

# 第一步: 查看磁盘使用情况
df -h

# 第二步: 找出占用空间最大的目录
sudo du -h / --max-depth=1 2>/dev/null | sort -rh | head -10

# 第三步: 深入查看大目录
sudo du -h /var --max-depth=1 | sort -rh | head -10

# 第四步: 找出最大的文件
sudo find / -type f -size +100M -exec ls -lh {} \; 2>/dev/null | awk '{print $5, $9}' | sort -rh | head -20

# 第五步: 查看已删除但仍被占用的文件
sudo lsof | grep deleted | sort -k7 -rn

# 第六步: 清理方案

# 清理包管理器缓存
sudo apt clean
sudo apt autoremove

# 清理日志(保留最近 100MB)
sudo journalctl --vacuum-size=100M

# 清理旧日志文件
sudo find /var/log -name "*.gz" -delete
sudo find /var/log -name "*.1" -delete

# 清理 Docker(如果使用)
docker system prune -a

# 清理 snap 旧版本
sudo snap list --all | awk '/disabled/{print $1, $3}' | while read name revision; do sudo snap remove "$name" --revision="$revision"; done

# 清理临时文件
sudo rm -rf /tmp/*
sudo rm -rf /var/tmp/*

# 清理缩略图缓存
rm -rf ~/.cache/thumbnails/*

# 第七步: 找出僵尸文件(已删除但进程仍打开)
sudo lsof +L1
# 需要重启相关进程来释放空间

9.3 网络不通排查

# 第一步: 检查网卡状态
ip addr show
# 确认网卡有 IP 地址

# 第二步: 检查网卡是否 up
ip link show
# 如果显示 DOWN:
sudo ip link set eth0 up

# 第三步: 检查本机网络
ping 127.0.0.1

# 第四步: 检查网关
ip route show
# 记下网关 IP,然后 ping
ping 192.168.1.1

# 第五步: 检查 DNS
cat /etc/resolv.conf
# 测试 DNS 解析
nslookup google.com
# 如果失败,尝试:
echo "nameserver 8.8.8.8" | sudo tee -a /etc/resolv.conf

# 第六步: 测试外网连通性
ping 8.8.8.8
# 如果通,说明网络正常,DNS 有问题

# 第七步: 检查防火墙规则
sudo iptables -L -n
sudo ufw status

# 第八步: 检查路由表
ip route show
netstat -rn

# 第九步: 追踪路由
traceroute google.com
mtr google.com

# 第十步: 检查网络服务
sudo systemctl status NetworkManager
sudo systemctl status systemd-networkd

9.4 进程僵死/卡死排查

# 第一步: 找到进程
ps aux | grep processname

# 第二步: 查看进程状态
ps -o pid,stat,cmd -p 1234
# STAT 含义:
# D: 不可中断睡眠(通常是 I/O)
# R: 运行中
# S: 睡眠
# T: 停止
# Z: 僵尸进程

# 第三步: 查看进程在做什么
sudo strace -p 1234

# 第四步: 查看进程的文件操作
sudo lsof -p 1234

# 第五步: 查看进程的系统调用
sudo cat /proc/1234/stack

# 第六步: 生成进程堆栈
sudo gdb -p 1234 -batch -ex "thread apply all bt"

# 第七步: 查看进程的 CPU 使用
top -p 1234

# 第八步: 尝试正常终止
kill -15 1234
# 等待 10 秒

# 第九步: 强制终止
kill -9 1234

# 如果是僵尸进程(Z 状态):
# 找到父进程
ps -o ppid= -p 1234
# 杀掉父进程
sudo kill -9 <ppid>

9.5 内存不足/泄漏排查

# 第一步: 查看内存使用
free -h
# 看 available 这一列

# 第二步: 找出内存占用最大的进程
ps aux --sort=-%mem | head -10

# 第三步: 查看进程的详细内存
sudo pmap -x 1234

# 第四步: 实时监控内存变化
watch -n 1 'ps aux --sort=-%mem | head -10'

# 第五步: 查看内存映射
sudo cat /proc/1234/smaps

# 第六步: 检查是否有内存泄漏
# 持续观察某个进程的内存占用
while true; do
  ps -o pid,vsz,rss,cmd -p 1234
  sleep 5
done

# 第七步: 查看 OOM 杀进程记录
sudo dmesg | grep -i "killed process"
sudo journalctl -k | grep -i "killed process"

# 第八步: 临时释放缓存(不推荐,除非紧急)
sudo sync
echo 3 | sudo tee /proc/sys/vm/drop_caches

9.6 高 CPU 占用排查

# 第一步: 找出 CPU 占用高的进程
top
# 按 P 按 CPU 排序

# 或者:
ps aux --sort=-%cpu | head -10

# 第二步: 查看进程的线程
top -H -p 1234
# 找出哪个线程占用高

# 第三步: 转换线程 ID 为十六进制
printf "%x\n" 1234

# 第四步: 查看线程堆栈
sudo cat /proc/1234/task/*/stack

# 第五步: 使用 perf 分析
sudo perf top -p 1234

# 或者记录 10 秒的性能数据
sudo perf record -p 1234 -g sleep 10
sudo perf report

# 第六步: 查看进程的系统调用
sudo strace -c -p 1234

# 第七步: 检查是否是死循环
sudo strace -p 1234 2>&1 | head -100

9.7 SSH 登录问题排查

# 场景一: 连接超时
# 检查网络连通性
ping server_ip

# 检查端口是否开放
nc -zv server_ip 22

# 检查防火墙
sudo ufw status

# 场景二: Permission denied
# 检查 SSH 服务状态
sudo systemctl status ssh

# 查看 SSH 日志
sudo tail -f /var/log/auth.log

# 检查 SSH 配置
sudo nano /etc/ssh/sshd_config
# 确认:
# PermitRootLogin yes/no
# PasswordAuthentication yes/no
# PubkeyAuthentication yes

# 重启 SSH 服务
sudo systemctl restart ssh

# 场景三: 密钥认证失败
# 检查密钥权限
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys

# 检查密钥内容
cat ~/.ssh/authorized_keys

# 在客户端测试连接
ssh -v user@server_ip
# 看详细输出找问题

9.8 文件系统问题排查

# 文件系统只读
# 查看挂载状态
mount | grep "on / "

# 重新挂载为读写
sudo mount -o remount,rw /

# 检查文件系统错误
sudo fsck /dev/sda1
# 注意: 必须先卸载分区或在救援模式下运行

# 查看磁盘健康状态
sudo smartctl -a /dev/sda

# 查看 I/O 错误
dmesg | grep -i error
sudo journalctl -k | grep -i "I/O error"

# 查看文件系统 inode 使用
df -i

# inode 用完的解决:
# 找出文件数量最多的目录
sudo find / -xdev -type f | cut -d "/" -f 2 | sort | uniq -c | sort -rn | head -10

# 删除不需要的小文件
sudo find /tmp -type f -delete

9.9 快速诊断脚本

创建一个一键诊断脚本:

#!/bin/bash
# system_check.sh - 系统快速诊断

echo "========== 系统基本信息 =========="
uname -a
uptime

echo -e "\n========== CPU 使用 TOP 5 =========="
ps aux --sort=-%cpu | head -6

echo -e "\n========== 内存使用 TOP 5 =========="
ps aux --sort=-%mem | head -6

echo -e "\n========== 内存状态 =========="
free -h

echo -e "\n========== 磁盘使用 =========="
df -h

echo -e "\n========== 磁盘 I/O =========="
iostat -x 1 2 | tail -n +4

echo -e "\n========== 网络连接统计 =========="
ss -s

echo -e "\n========== 监听端口 =========="
ss -tlnp

echo -e "\n========== 最近的系统错误 =========="
journalctl -p err -n 10 --no-pager

echo -e "\n========== 最近登录记录 =========="
last -n 10

echo -e "\n========== 失败的服务 =========="
systemctl --failed

使用:

chmod +x system_check.sh
sudo ./system_check.sh

十、进阶技巧与最佳实践

10.1 别名(Alias)提高效率

设置常用别名:

# 编辑 bash 配置
nano ~/.bashrc

# 在文件末尾添加:
alias ll='ls -alh'
alias la='ls -A'
alias l='ls -CF'
alias ..='cd ..'
alias ...='cd ../..'
alias grep='grep --color=auto'

# 常用系统命令别名
alias update='sudo apt update && sudo apt upgrade'
alias install='sudo apt install'
alias remove='sudo apt remove'
alias search='apt search'

# 快速查看日志
alias syslog='sudo tail -f /var/log/syslog'
alias authlog='sudo tail -f /var/log/auth.log'

# 网络相关
alias ports='sudo netstat -tulnp'
alias listening='sudo lsof -i -P -n | grep LISTEN'

# 进程相关
alias psmem='ps aux --sort=-%mem | head'
alias pscpu='ps aux --sort=-%cpu | head'

# Docker 相关(如果使用)
alias dps='docker ps'
alias dpa='docker ps -a'
alias di='docker images'
alias dlog='docker logs -f'

# Git 相关
alias gs='git status'
alias ga='git add'
alias gc='git commit'
alias gp='git push'
alias gl='git log --oneline'

# 使别名生效
source ~/.bashrc

# 查看所有别名
alias

# 临时禁用别名(使用原始命令)
\ls  # 前面加反斜杠

10.2 环境变量管理

# 查看所有环境变量
env
printenv

# 查看特定变量
echo $PATH
echo $HOME

# 临时设置变量(当前 shell 有效)
export MY_VAR="value"

# 永久设置(当前用户)
nano ~/.bashrc
# 添加:
export MY_VAR="value"
export PATH=$PATH:/opt/myapp/bin

# 永久设置(所有用户)
sudo nano /etc/environment
# 添加:
MY_VAR="value"

# 使变量生效
source ~/.bashrc

# 删除变量
unset MY_VAR

10.3 输入输出重定向

# 标准输出重定向
ls > output.txt         # 覆盖
ls >> output.txt        # 追加

# 标准错误重定向
command 2> error.txt

# 同时重定向输出和错误
command > output.txt 2>&1
command &> output.txt    # 简写

# 分别重定向
command > output.txt 2> error.txt

# 丢弃输出
command > /dev/null 2>&1

# 输入重定向
command < input.txt

# Here Document
cat << EOF > file.txt
line 1
line 2
EOF

# Here String
grep "pattern" <<< "test string"

10.4 管道与命令组合

# 管道: 前一个命令的输出作为后一个的输入
ps aux | grep nginx | awk '{print $2}'

# 统计行数
ps aux | wc -l

# 排序并去重
cat file.txt | sort | uniq

# 计数重复行
cat file.txt | sort | uniq -c

# xargs: 将标准输入转为命令参数
find . -name "*.tmp" | xargs rm
find . -name "*.log" -print0 | xargs -0 rm  # 处理文件名有空格的情况

# tee: 同时输出到屏幕和文件
ls -la | tee output.txt

# 命令替换
echo "Today is $(date)"
echo "Files: $(ls | wc -l)"

# 命令组合
cd /var/log && ls -lh  # 前面成功才执行后面
cd /nonexist || echo "Failed"  # 前面失败才执行后面
mkdir test && cd test  # 常用组合

10.5 循环与条件

在命令行中使用循环:

# for 循环
for i in {1..5}; do echo "Number $i"; done

# 遍历文件
for file in *.txt; do echo "Processing $file"; done

# while 循环
i=1; while [ $i -le 5 ]; do echo $i; ((i++)); done

# 批量重命名
for file in *.txt; do mv "$file" "${file%.txt}.md"; done

# 批量下载
for i in {1..10}; do wget "http://example.com/file$i.zip"; done

# 条件判断
if [ -f file.txt ]; then echo "File exists"; else echo "Not found"; fi

# 检查目录
if [ -d /var/log ]; then cd /var/log; fi

# 检查命令是否存在
if command -v docker &> /dev/null; then echo "Docker installed"; fi

10.6 脚本调试

# 显示执行的每一条命令
bash -x script.sh

# 在脚本开头添加
#!/bin/bash
set -x  # 显示执行的命令
set -e  # 遇到错误立即退出
set -u  # 使用未定义变量时报错
set -o pipefail  # 管道中任何命令失败都算失败

# 或者一行搞定
set -euxo pipefail

# 调试特定部分
set -x
# 要调试的代码
set +x

# 使用 shellcheck 检查脚本
sudo apt install shellcheck
shellcheck script.sh

10.7 文本处理神器

sed 实用示例:

# 替换文本(不修改原文件)
sed 's/old/new/' file.txt

# 替换所有匹配(不只是第一个)
sed 's/old/new/g' file.txt

# 直接修改文件
sed -i 's/old/new/g' file.txt

# 删除空行
sed '/^$/d' file.txt

# 删除包含某个词的行
sed '/pattern/d' file.txt

# 在第 3 行后插入内容
sed '3a\New line' file.txt

# 替换第 5 行
sed '5c\New content' file.txt

# 只处理第 2 到第 5 行
sed '2,5s/old/new/g' file.txt

awk 实用示例:

# 打印第一列
awk '{print $1}' file.txt

# 打印第一和第三列
awk '{print $1, $3}' file.txt

# 使用不同的分隔符
awk -F ':' '{print $1}' /etc/passwd

# 条件过滤
awk '$3 > 100' file.txt

# 计算总和
awk '{sum += $1} END {print sum}' file.txt

# 统计行数
awk 'END {print NR}' file.txt

# 格式化输出
awk '{printf "%-10s %s\n", $1, $2}' file.txt

# Nginx 日志分析: 统计状态码
awk '{print $9}' access.log | sort | uniq -c | sort -rn

# 统计 IP 访问量
awk '{print $1}' access.log | sort | uniq -c | sort -rn | head -10

# 计算平均响应时间
awk '{sum+=$NF} END {print sum/NR}' time.log

10.8 定时任务进阶

# 编辑 crontab
crontab -e

# Crontab 格式:
# ┌─────── 分钟 (0-59)
# │ ┌───── 小时 (0-23)
# │ │ ┌─── 日期 (1-31)
# │ │ │ ┌─ 月份 (1-12)
# │ │ │ │ ┌ 星期 (0-6, 0=周日)
# │ │ │ │ │
# * * * * * command

# 实用示例:
# 每分钟执行
* * * * * /path/to/script.sh

# 每 5 分钟
*/5 * * * * /path/to/script.sh

# 每小时的第 30 分钟
30 * * * * /path/to/script.sh

# 每天凌晨 2 点
0 2 * * * /path/to/script.sh

# 每周一上午 9 点
0 9 * * 1 /path/to/script.sh

# 每月 1 号凌晨 3 点
0 3 1 * * /path/to/script.sh

# 工作日早上 8 点
0 8 * * 1-5 /path/to/script.sh

# 重定向输出和错误
0 2 * * * /path/to/script.sh >> /var/log/backup.log 2>&1

# 设置环境变量
0 2 * * * export PATH=/usr/local/bin:$PATH && /path/to/script.sh

# 查看 cron 日志
sudo tail -f /var/log/syslog | grep CRON

# 系统级定时任务(在 /etc/cron.d/ 创建文件)
sudo nano /etc/cron.d/myapp
# 格式: 分 时 日 月 周 用户 命令
0 2 * * * root /path/to/script.sh

10.9 SSH 进阶技巧

# SSH 密钥认证
ssh-keygen -t rsa -b 4096 -C "[email protected]"
ssh-copy-id user@server

# SSH 配置文件(~/.ssh/config)
nano ~/.ssh/config

# 内容示例:
Host myserver
    HostName 192.168.1.100
    User ubuntu
    Port 22
    IdentityFile ~/.ssh/id_rsa

# 使用: ssh myserver

# SSH 端口转发
# 本地转发: 访问本地 8080 -> 转发到远程 80
ssh -L 8080:localhost:80 user@server

# 远程转发: 远程访问 8080 -> 转发到本地 80
ssh -R 8080:localhost:80 user@server

# 动态转发(SOCKS 代理)
ssh -D 1080 user@server

# 保持连接
# 在 ~/.ssh/config 添加:
Host *
    ServerAliveInterval 60
    ServerAliveCountMax 3

# SSH 跳板机
ssh -J jumphost targethost

# 后台运行 SSH 连接
ssh -fN -L 8080:localhost:80 user@server

# SCP 文件传输
scp file.txt user@server:/path/to/destination
scp -r directory/ user@server:/path/
scp user@server:/path/file.txt ./

# rsync 同步(更强大)
rsync -avz source/ user@server:/destination/
rsync -avz --delete source/ user@server:/destination/  # 删除目标多余文件
rsync -avz -e "ssh -p 2222" source/ user@server:/dest/  # 指定端口

10.10 实用单行命令

# 快速启动 HTTP 服务器(分享文件)
python3 -m http.server 8000

# 生成随机密码
openssl rand -base64 16

# 查看哪些用户在线
w
who

# 查看登录历史
last
last -10  # 最近 10 次

# 查看失败的登录尝试
sudo lastb

# 显示文件的十六进制
xxd file.bin

# 对比两个文件
diff file1.txt file2.txt
diff -u file1.txt file2.txt  # 统一格式
vimdiff file1.txt file2.txt  # 可视化对比

# 批量替换文件内容
find . -name "*.txt" -exec sed -i 's/old/new/g' {} \;

# 查找并删除空目录
find . -type d -empty -delete

# 查看文件的创建/修改时间
stat file.txt

# 快速备份文件
cp important.conf{,.backup}

# 创建目录并进入
mkdir -p project/src && cd $_

# 上一个命令的最后一个参数
echo $_

# 清空文件但保留文件
> file.txt

# 比较两个目录
diff -rq dir1/ dir2/

# 监控文件变化
watch -d -n 1 'ls -lh file.txt'

# 查看进程树
pstree -p

# 在后台解压大文件
nohup tar -xzf large.tar.

命令行的学习是一个持续的过程,不需要一次记住所有命令。建议:

  1. 收藏这篇文章,遇到问题时快速查找
  2. 用到什么学什么,不要死记硬背
  3. 使用 Tab 键自动补全,提高效率减少错误
  4. 建立自己的命令片段库,把常用的命令保存到笔记里

善用 --helpman:大部分命令都有帮助文档

nginx --helpman systemctl

最重要的是:不要怕敲错命令,Linux 不会轻易让你搞坏系统(除非你 sudo rm -rf /)。多练习,你很快就能熟练掌握这些工具。

Read more

心智难民

心智难民

心智,按照牛津词典的定义,是获取和运用知识的能力。 互联网是一场技术革命,给每个人提供了机会。社会是由阶层组成的,每一场技术革命都促使了不同阶层的重新洗牌,或者说阶层分化。网络世界的阶层分化是什么样的呢?大概可以分为两个大的阶层:一类是接受高质量信息的精英阶层,另外一类是消费网络上的垃圾信息、接受劣质信息的乌合之众。 当然,这里说的“免费”是打引号的。因为它不仅不免费,而且一点也不便宜。 人们喜欢免费的东西。但是世界上除了阳光和空气,没什么是真正免费的东西,只是支付的方式不一样——有的直接用钱付,有的间接用钱付;有些用生活质量付,有些用人生的潜力和机会付。 You must pay for everything in this world, one way or another. Nothing is free. 你终究会以不同的方式付费,天下没有免费的午餐。 如果一个人只接受网上“免费”的信息,就像是只吃劣质食品一样,结果就是精神世界的劣质化。因为接受信息质量的差异,

By 王圆圆
Crazy World

Crazy World

by Jeff Daniels 译文 我看见一个年轻女孩笑了, 因为他刚说的话。 我看着他坠入她那双美丽的眼睛里, 脸红的像玫瑰。 我看见一位老人在走路, 妻子陪在他身旁。 我看着他俯身握住她的手, 天啊,我竟然哭了。 这疯狂的世界越来越疯狂, 我有什么资格评判呢? 但值得庆幸的是, 在这个充满仇恨的世界里, 还有人在用心相爱着。 我看见狗摇着尾巴, 看见孩子在奔跑。 我也曾在无数个日落里, 对着夕阳唱着歌。 我看见有人为别人扶着门, 看见陌生人握手寒暄。 我看见她和那个曾经错过的旧情人拥吻, 时间比计划中的更长了一些。 这个疯狂的世界继续疯狂着, 但我能说什么? 好在这个充满恨的世界里, 还有人在用心相爱着。 我看见祈祷被回应, 看见了六月里的新娘。 我骄傲地说,我当时见到了银河, 对着月光下的人们闪烁。 我看见送出的一打玫瑰, 见过她满心的欢喜藏不住, 我见过的已经足够, 让我明白我所知道的, 也坚信我依然相信的。 这疯狂的世界越来越疯狂, 我能说什么? 但值得庆幸的是, 在这个充满仇恨的世界里, 还有人相爱着。 原文 I’ve seen a

By 王圆圆
人是能被改变的吗?

人是能被改变的吗?

想改变别人基本上是在浪费时间。这个话题听起来简单,但仔细想想,我们生活中有太多时候都在做这种徒劳的事。 生活中的人大概可以分成三类: 喜欢的人 - 这些人即使有缺点你也能接受。你们相处舒服,他们做什么你都能理解,就算偶尔看不惯,也不会想着要去改造他们。 无所谓的人 - 占了我们生活中的大多数。同事、路人、网上的陌生人,他们怎么生活、怎么思考,其实跟你一点关系都没有。 讨厌的人 - 那些让你感到不舒服的人。可能是价值观完全相反,可能是行为方式你无法忍受。 既然人际关系本来就是这样,为什么还要费劲去改变谁呢?尤其是那些无所谓的人和讨厌的人,你花时间去说服他们、纠正他们,最后累的是自己。有这个功夫,不如多看两本书,学点新东西,改变一下自己。 美国人教小孩一个词:Walk Away。意思就是遇到麻烦的人、不讲理的人,转身走就完了,不用纠缠。 这听起来好像是逃避,但其实是一种很成熟的处理方式。你不是害怕对方,而是知道跟这种人浪费时间没有意义。 有个作家Charles Portis说过一句话挺有意思的:"

By 王圆圆
留守的代价

留守的代价

我有一个90后的朋友,她的故事让我久久无法平静。 她13岁那年,初中还没读完就辍学了,跟着同乡去了南方打工。六年后,在家人的安排下,她嫁给了邻村一个老实人家的儿子。没有恋爱,没有了解,只有两个家庭觉得"差不多,能过"的判断。 婚后他们一起在宁波工作,陆续有了两个女儿。按理说,一家四口,日子虽苦但也算完整。但我们那个地方,重男轻女的观念像一只看不见的手,推着她生下了第三个孩子——终于是个儿子。 三个孩子陆续到了上学的年龄,他们却一直在外打工。孩子成了留守儿童,跟着爷爷奶奶在老家,一年见父母一两次。视频通话里,孩子越来越沉默,成绩越来越差,老师反映性格也出现了问题。 她做了一个决定:回家照顾孩子。 他继续在外地送快递。从此,这个家庭被一分为二——一边是她独自面对三个问题儿童的混乱和辛苦,一边是他在城市里每天十几个小时的奔波劳累。 本来就没什么感情基础的两个人,在这种分离中,最后那点维系也消磨殆尽了。 最近两年,他给家里的生活费越来越少。后来她才知道,他在外面有了别人,赚的钱不多,都花在了新欢身上。

By 王圆圆