🐧 从零玩转 Linux 命令行:一位后端菜鸟的 9 天硬核笔记
最近正在系统性地补计算机基础,第一块啃的是 Linux 命令行。
虽然是后端开发,日常也跟服务器打交道,但以前都是"知其然而不知其所以然"。
这一周从 FHS 目录结构开始,到系统巡检脚本收尾,花了 9 天把 Linux 基础过了一遍。
下文按"命令 → 说明 → 执行结果"来组织,方便对照学习。
一、Linux 的目录结构
第一次打开终端敲 ls -la /,面对那一大堆目录,我当时是懵的:/bin、/sbin、/usr、/etc、/var、/tmp、/proc、/dev……这都什么跟什么?
其实这些目录的摆放有标准叫 FHS(Filesystem Hierarchy Standard)。Linux 圈子商量好了——大家按规矩来,某些目录放某些东西。这样换发行版也不用重新学。
根目录结构
ls -la /
以长格式 + 隐藏文件方式列出根目录下所有内容。这是你认识 Linux 的第一步。
$ ls -la /
total 2044520
drwxr-xr-x 20 root root 4096 Jun 8 10:00 .
lrwxrwxrwx 1 root root 7 Jun 1 2024 bin –> usr/bin
drwxr-xr-x 4 root root 4096 Jun 1 2024 boot
drwxr-xr-x 144 root root 12288 Jun 8 10:00 etc
drwxr-xr-x 3 root root 4096 Jun 1 2024 home
dr-xr-xr-x 289 root root 0 Jun 8 09:29 proc
drwxrwxrwt 23 root root 4096 Jun 8 11:00 tmp
drwxr-xr-x 11 root root 4096 Jun 1 2024 usr
drwxr-xr-x 13 root root 4096 Jun 1 2024 var
注意 bin、sbin、lib 前面有个 l(链接符号),说明它们是指向 /usr/bin 等目录的软链接。这是新发行版的趋势——把根目录下的东西合并到 /usr,减少混乱。
各目录的职责:
| /bin /sbin | 系统命令 | 启动就得用的,如 ls、fdisk |
| /usr | 用户软件 | 相当于 Windows 的 Program Files |
| /etc | 配置文件 | Nginx、MySQL 配置都在这 |
| /var | 可变数据 | 日志在 /var/log |
| /tmp | 临时文件 | 重启就清空 |
| /proc /sys | 虚拟文件系统 | 内核实时信息,不占磁盘 |
| /dev | 设备文件 | 硬件也是文件 |
| /home | 用户家目录 | 各用户自己的空间 |
/proc 和 /sys 很特别——它们不占用磁盘空间,是内核运行时映射出来的。你读它们等于在读内核的实时状态。这种"一切皆文件"的设计是 Unix 哲学的核心理念。
查看文件信息
ls -lh
以长格式 + 人类易读大小列出文件。-l 显示权限/所有者/大小/时间,-h 把字节数转成 K/M/G。
$ ls -lh
total 24M
-rw-r–r– 1 hengjun devops 1.2K Jun 8 10:00 app.log
-rw-r–r– 1 hengjun devops 24M Jun 7 12:00 dump.sql
ls -ltr
按修改时间倒序排列(旧的在上)。查日志时按时间顺序追踪问题很好用。
ls -lS
按文件大小从大到小排列。
stat file
查看文件的完整元数据——Inode(唯一编号)、Links(硬链接数)、三个时间戳。
$ stat app.log
File: app.log
Size: 245890 Inode: 1234567 Links: 1
Access: 2024-06-08 10:00:00
Modify: 2024-06-08 11:30:00
Change: 2024-06-08 11:30:00
内容没变但 Change 时间更新了?说明有人动过权限。
file file
根据文件头的"魔数"判断真实类型。Linux 不看后缀名。
$ file app.jar
app.jar: Java archive data (ZIP)
$ file logo.png
logo.png: PNG image data, 800 x 600, 8-bit/color RGBA
通配符
ls *.log
* 匹配任意多个字符,列出所有 .log 结尾的文件。
ls ?.txt
? 匹配恰好一个字符。a.txt 能匹配,long.txt 不行。
ls file[123].txt
[] 匹配括号内的某个字符。
mkdir -p src/{main,test}/{java,resources}
花括号批量展开,一键创建 4 个目录。
帮助系统
man ls
打开 ls 的完整参考手册(按 q 退出)。
ls –help
快速查看 ls 的用法摘要。
which ls
查看 ls 命令的绝对路径。
type ls
查看 ls 是否为别名。
whereis ls
连源码位置和 man 手册位置一起显示。
二、文件操作命令
创建和删除
touch file
创建空文件。文件已存在则刷新其时间戳。
mkdir -p a/b/c
创建多级目录,中间目录不存在就自动创建,不会报错。
rm -rf dir/
递归强制删除目录。没有回收站,敲之前一定 ls 确认路径!
dd if=/dev/zero of=test bs=1M count=100
底层数据拷贝工具,常用来创建指定大小文件或测磁盘速度。
$ dd if=/dev/zero of=testfile bs=1M count=100
100+0 records in, 100+0 records out
104857600 bytes (105 MB) copied, 0.345 s, 303 MB/s
复制和移动
cp source dest
复制文件到目标位置。
cp -r src/ dst/
递归复制整个目录。
cp -a src/ dst/
完整复制(保留权限、时间戳、软链接),相当于完整克隆。
cp -p src dst
保留原文件的修改时间。
$ cp -p source.txt dest.txt
$ ls -l source.txt dest.txt
-rw-r–r– 1 hengjun devops 456 Jun 8 10:00 source.txt
-rw-r–r– 1 hengjun devops 456 Jun 8 10:00 dest.txt # 时间保留了!
mv src dst
移动或重命名文件。Linux 里"重命名"本质就是移动。
权限管理——先搞懂基本概念
在 Linux 里,每个文件都有三组权限,分别针对三种角色:
| u | user | 文件所有者(你是这个文件的主人) |
| g | group | 所属组(和你在同一个团队的人) |
| o | other | 其他人(既不是主人也不在团队里的人) |
每组权限都有三种操作:
| r | 4 | 读文件内容 | 列出目录下的文件列表 |
| w | 2 | 修改文件内容 | 在目录里增删文件 |
| x | 1 | 执行文件(脚本/程序) | 进入目录(cd 进去) |
三种角色 × 三种权限,用 9 个字符表示。看一个实际的例子:
$ ls -l script.sh
-rwxr-xr– 1 hengjun devops 1234 Jun 8 10:00 script.sh
# ↑——— 权限位,拆开来看:
# – (文件类型)
# rwx (所有者 u:可读可写可执行)
# r-x (所属组 g:可读可执行,不能写)
# r– (其他人 o:只能读)
第一位的 – 是文件类型,常见的还有 d(目录)、l(链接)。后面九位每三位一组,分别是 u、g、o 的权限,每组按 rwx 顺序排列,有权限就写字母,没有就写 -。
修改权限有两种方式,理解透了才能随手写出正确的命令。
方式一:符号模式——用字母表达,直观好懂
格式:chmod 角色 操作 权限 文件
| u | 所有者 | + | 加上某权限 |
| g | 所属组 | – | 去掉某权限 |
| o | 其他人 | = | 设为指定的权限(覆盖之前的) |
| a | 全部三种角色 |
# 给 script.sh 的所有者(u)加(+)执行权限(x)
$ chmod u+x script.sh
# 去掉所属组(g)的写权限(w)
$ chmod g-w app.log
# 把其他人(o)的权限设(=)为只读(r)
$ chmod o=r notes.txt
# 一次性设置多组:所有者全部权限,组给读+执行,其他人不给任何权限
$ chmod u=rwx,g=rx,o= notes.txt
方式二:数字模式——基于权限编码,写着快
每种权限对应一个数字:r=4、w=2、x=1。把三个数字加起来,就得到这组权限的编码。
r + w + x = 4 + 2 + 1 = 7
r + x = 4 + 0 + 1 = 5
r = 4 + 0 + 0 = 4
然后三个数字分别代表 u、g、o 的权限:
# 755 = 所有者7(rwx) + 组5(r-x) + 其他人5(r-x)
$ chmod 755 script.sh
$ ls -l script.sh
-rwxr-xr-x 1 hengjun devops 123 Jun 8 10:00 script.sh
# 644 = 所有者6(rw-) + 组4(r–) + 其他人4(r–)
$ chmod 644 app.log
$ ls -l app.log
-rw-r–r– 1 hengjun devops 1.2K Jun 8 10:00 app.log
# 600 = 所有者6(rw-) + 组0(—) + 其他人0(—)
$ chmod 600 id_rsa
$ ls -l id_rsa
-rw——- 1 hengjun devops 1679 Jun 1 2024 id_rsa
常见的权限组合:
| 755 | rwxr-xr-x | 可执行文件、目录(最常见的可执行权限) |
| 644 | rw-r–r– | 普通文件(最常见的文件权限) |
| 600 | rw——- | SSH 私钥、配置文件(只有自己能看) |
| 700 | rwx—— | 私人脚本(只有自己能执行) |
| 777 | rwxrwxrwx | 不推荐——所有用户都能改,安全隐患 |
特别注意:SSH 私钥必须设成 600(rw——-),否则 SSH 会直接拒绝使用,提示 Permissions 0644 for 'id_rsa' are too open. 这是为了阻止其他用户私钥。
chown user:group file
修改文件的所有者和所属组。user 是新的所有者,group 是新的所属组。
# 把 file.txt 的所有者设为 zhangsan
$ sudo chown zhangsan file.txt
# 同时修改所有者和组
$ sudo chown zhangsan:devops file.txt
# 递归修改整个目录
$ sudo chown -R zhangsan:devops /opt/app/
chgrp group file
只修改文件的所属组,不改所有者。
umask
查看或设置"默认权限掩码"。为什么新创建的文件自动就是 644,目录就是 755?因为 umask 在背后起作用。
$ umask
0022
umask 的计算规则:最终权限 = 最大权限 – umask。文件的"最大权限"是 666(因为文件默认不给执行权限),目录的最大权限是 777。
umask=022 时:
文件:666 – 022 = 644(rw-r–r–)
目录:777 – 022 = 755(rwxr-xr-x)
umask=027 时(更安全):
文件:666 – 027 = 640(rw-r—–)
目录:777 – 027 = 750(rwxr-x—)
特殊权限
三种特殊权限挂在普通权限之上,用的场景特定,但遇到了要认识。
chmod u+s myapp
SUID(Set User ID)。设置了 SUID 的可执行文件,任何人执行它时都临时获得文件所有者的身份。最经典的例子是 /usr/bin/passwd:
$ ls -l /usr/bin/passwd
-rwsr-xr-x 1 root root 68208 Jun 1 2024 /usr/bin/passwd
# ^ 注意:所有者的 x 变成了 s
普通用户改密码时,需要写入 /etc/shadow 文件,但这个文件属于 root,普通用户本来没有权限写。passwd 设置了 SUID,所以普通用户执行它时就临时"变身"成 root,改完密码就退回去。
chmod g+s /shared/dir
SGID(Set Group ID)。对目录设置 SGID 后,在该目录下新建的文件自动继承目录的所属组。团队协作时特别有用——大家创建一个共享目录,设置 SGID 后,所有成员在里面创建的文件都属于同一个组,不需要每个人手动 chgrp。
$ sudo mkdir /shared/project
$ sudo chgrp devops /shared/project
$ sudo chmod g+s /shared/project
$ ls -ld /shared/project
drwxrwsr-x 2 root devops 4096 Jun 8 10:00 /shared/project
# ^ 组的 x 变成了 s
chmod +t /tmp
Sticky Bit。设置了 Sticky Bit 的目录,任何用户都可以写文件,但只能删除自己的文件。/tmp 就是最典型的例子——任何人都能在这里创建临时文件,但不能删别人的文件。
$ ls -ld /tmp
drwxrwxrwt 23 root root 4096 Jun 8 11:00 /tmp/
# ^ 其他人的 x 变成了 t
硬链接和软链接
这两个概念新手很容易搞混,我用大白话解释一下。
先想一个问题:Linux 里存一个文件,到底需要几样东西?
答案是两样:①文件内容本身(就是那一堆数据),②一个"名字标签"指向这份数据。这个"名字标签"在 Linux 里就叫"文件名",一个文件内容可以有好几个名字标签。
打个比方:你家小区里住了个叫"张三"的人。硬链接和软链接的区别就像是:
硬链接 = 张三有两个外号,“三哥"和"阿三”。不管叫哪个名字,找的都是同一个人。你把"三哥"这个名字划掉了(删了),张三还在,别人叫"阿三"照样能找到他。
软链接 = 墙上贴了张纸条写着"张三家在 3 单元 201"。你顺着纸条能找到他家。但如果张三搬走了(原文件删了),纸条还在但人没了,你就找不到了。
来看实际操作:
ln original.txt hardlink.txt
创建硬链接——相当于给同一份文件内容起了另一个名字。两个名字指向同一份数据,改一个另一个也跟着变。
ln -s original.txt softlink.txt
创建软链接(符号链接)——相当于创建一个快捷方式,里面只记了"original.txt 这个路径"。原文件删了,快捷方式就废了。
# 先创建一个原始文件
$ echo "Hello World" > original.txt
# 分别创建硬链接和软链接
$ ln original.txt hardlink.txt
$ ln -s original.txt softlink.txt
# 看看效果
$ ls -l
-rw-r–r– 2 hengjun devops 12 Jun 8 10:00 original.txt
-rw-r–r– 2 hengjun devops 12 Jun 8 10:00 hardlink.txt
lrwxrwxrwx 1 hengjun devops 12 Jun 8 10:00 softlink.txt –> original.txt
# ↑ 看到了吗?
# 软链接会显示一个箭头 -> 指向它"引用的文件"
# 硬链接和原文件一模一样,光看 ls 分不出来
# 关键实验:删掉原文件
$ rm original.txt
# 硬链接还能用!
$ cat hardlink.txt
Hello World
# 软链接废了!
$ cat softlink.txt
cat: softlink.txt: No such file or directory
这就很清楚了——硬链接是"另一个名字",软链接是"指向那个名字的牌子"。牌子指向的名字被删了,牌子就没用了。
什么时候用哪个?
| 给一个文件起多个名字 | 硬链接 | 省空间,两个名字指向同一份数据 |
| 跨文件系统链接 | 软链接 ✅ | 硬链接不能跨文件系统 |
| 链接目录 | 软链接 ✅ | 硬链接不能链接目录 |
| 做个快捷方式方便访问 | 软链接 ✅ | 比如把 /var/log/nginx/access.log 软链接到 ~/logs/ |
| 删其中一个不影响另一个 | 硬链接 ✅ | 删一个名字,数据还在 |
实际工作中,软链接用得比硬链接多得多。最常见的用法:部署新版本的时候,创建一个 current -> v2.0 的软链接,回滚时改成 current -> v1.0,这就是很多部署工具(如 Capistrano)的原理。
三、文本查看与搜索
做后端开发的,查日志占日常工作的很大一部分。
文件浏览
nl file
带行号输出文件内容。
head -n 50 file
查看文件前 50 行。
tail -n 100 file
查看文件最后 100 行。
tail -f file
实时追踪文件新写入的内容。排查线上问题的头号神器。
tail -f app.log | grep ERROR
只显示错误日志的实时流,干净利落。
less file
分页查看文件。按 / 搜索关键字、n 跳到下一个、g/G 到首尾、q 退出。
grep 基础搜索
grep "pattern" file
找出文件中所有包含 pattern 的行。
$ grep "ERROR" app.log
2024-06-08 10:00:01.567 ERROR PaymentService – 支付超时: 订单 98765
2024-06-08 10:00:02.001 ERROR PaymentService – 余额不足: 用户 12345
grep -v "pattern" file
反向匹配——排除某些"噪音"日志。
grep -c "pattern" file
只统计匹配数量,适合脚本中做告警判断。
$ grep -c "ERROR" app.log
3
grep -i "pattern" file
忽略大小写搜索。
grep -n "pattern" file
显示匹配行号。
grep 上下文搜索
grep -C 5 "pattern" file
显示匹配行前后各 5 行。排查问题时几乎必用——只看单独一行 ERROR 信息不够。
$ grep -C 1 "余额不足" app.log
2024-06-08 10:00:01.567 ERROR PaymentService – 支付超时: 订单 98765
2024-06-08 10:00:02.001 ERROR PaymentService – 余额不足: 用户 12345
2024-06-08 10:00:02.345 INFO PaymentService – 用户 12345 余额查询
grep -A 20 "Exception" file
显示匹配行后面 20 行,看 Java 异常堆栈够用了。
grep -B 5 "ERROR" file
显示匹配行前面 5 行。
grep 递归和正则
grep -r "TODO" src/
在目录中递归搜索。
grep -r –include="*.java" "FIXME" src/
只搜 Java 文件。
grep -r -l "BeanUtils" src/
只输出匹配的文件名。
grep -E "pattern" file
使用扩展正则表达式。
grep -oP "pattern" file
使用 Perl 风格正则 + 只输出匹配的部分。
$ grep -oP "订单 \\d+" app.log
订单 98765
订单 98766
四、管道与文本处理
单个命令能力有限,用 | 把命令串起来就能做出强大的数据处理流水线。
排序和去重
sort -n file
按数字大小排序。不加 -n 按字母序,会把 11 排在 2 前面。
sort -rn file
数字倒序排。
sort -k2 -t: file
按第二列排序、冒号分隔。
sort -u file
排序加去重。
uniq -c file
统计每行出现次数。重点:必须先 sort,因为 uniq 只能去重相邻的行。
$ sort ips.txt | uniq -c
4 192.168.1.1
1 192.168.1.2
1 192.168.1.3
数据裁剪和转换
cut -d',' -f1,3 file
按分隔符裁剪列。
tr 'a-z' 'A-Z'
字符替换——小写转大写。
tr -d '\\r' < win.txt > unix.txt
删除 Windows 回车符。
管道分叉
tee
把数据同时写到文件和屏幕。
$ cat access.log | grep "ERROR" | tee errors.txt | wc -l
42
# 屏幕显示 42,errors.txt 存了完整的 42 行错误日志
管道组合实战
cat access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head -10
统计日志中访问最多的 10 个 IP。
$ cat access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head -5
4823 192.168.1.100
1209 10.0.0.55
876 203.0.113.42
最上面的 IP 远超其他,很可能是爬虫或攻击。
du -sh /* | sort -rh | head -5
找出占用磁盘最大的 5 个目录。
$ du -sh /* | sort -rh | head -5
4.2G /usr
1.8G /var
456M /opt
ss -tan | awk '{print $1}' | sort | uniq -c | sort -rn
查看 TCP 连接状态分布。
$ ss -tan | awk '{print $1}' | sort | uniq -c | sort -rn
245 ESTAB
32 TIME-WAIT # 太多说明短连接频繁
8 LISTEN
5 CLOSE-WAIT # 程序没正确关闭连接才会出现
重定向
echo "Hello" > file
覆盖写入文件。
echo "World" >> file
追加写入文件。
ls /nonexistent 2> error.log
重定向标准错误(文件描述符 2)到文件。
nohup java -jar app.jar > app.log 2>&1 &
生产环境部署的经典写法:nohup 忽略退出终端信号,> app.log 标准输出写日志,2>&1 错误输出也写进同一日志,& 后台运行。
command > /dev/null 2>&1
完全丢弃所有输出。/dev/null 是 Linux 的"黑洞"。
五、进程管理
进程查看
ps aux
列出所有进程的快照。
$ ps aux | head -3
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.4 168352 11732 ? Ss Jun08 0:23 /sbin/init
hengjun 1234 2.3 8.5 4281592 245680 ? Sl Jun08 23:45 java -jar app.jar
PID 是进程 ID,%CPU 是单核占比(超 100% 说明多线程),RSS 是实际物理内存(以这个为准)。STAT 中 R 运行中、S 休眠、D 不可中断(大量 D 说明磁盘瓶颈)、Z 僵尸(说明有 Bug)。
ps aux | grep java
快速找 Java 进程的 PID。
ps -T -p 1234
查看进程 1234 内的所有线程。
top
实时监控面板,每 3 秒刷新。按 P 按 CPU 排序,M 按内存排序,1 展开各 CPU 核心,H 切线程模式。
$ top -bn1 | head -3
top – 11:02:34 up 2 days, load average: 0.08, 0.12, 0.10
%Cpu(s): 2.3 us, 0.5 sy, 0.2 wa
load average 超过 CPU 核心数说明 CPU 不够用。wa 是等待 IO 占比,持续偏高说明磁盘是瓶颈。
进程控制
kill -15 PID
发 SIGTERM(优雅终止)。进程可以捕获信号做清理——关闭连接、刷新缓冲区。优先使用。
kill -9 PID
发 SIGKILL(强制杀死)。进程没机会做清理,可能导致数据丢失。不能上来就用。 正确流程:先 -15 尝试优雅关闭,不行再 -15,最后无奈才 -9。
kill -3 PID
发 SIGQUIT——不杀进程,只让 Java 输出线程 dump(排查死锁的黄金证据)。
nohup cmd &
在后台运行进程,退出终端也不终止。
Ctrl+Z → bg → fg
前后台切换:Ctrl+Z 暂停,bg 放后台继续,fg 拉回前台,jobs 查看后台任务。
进程优先级
nice -n 10 ./cmd
以低优先级启动(nice 值越高越谦让,范围 -20~19,默认 0)。
renice -n 5 -p PID
运行时调整优先级。普通用户只能调高 nice 值(更谦让),不能调低。
六、网络工具与包管理
网络排查四步走
ping -c 4 host
看机器通不通。看两个指标:丢包率和延迟。
$ ping -c 4 baidu.com
64 bytes from 39.156.66.10: icmp_seq=1 ttl=128 time=12.3 ms
4 packets transmitted, 4 received, 0% packet loss
0 丢包说明网络基本正常。ping 不通先查网线和 IP 配置。
nc -zv host port
看端口开没开。nc(netcat)比 telnet 更轻量。
$ nc -zv 192.168.1.100 3306
Connection to 192.168.1.100 3306 port [tcp/mysql] succeeded!
nc -zv -w 3 host 1-1000
扫描 1-1000 端口。
dig host +short
看域名解析。解析错了啥都连不上。
$ dig baidu.com +short
39.156.66.10
ss -tlnp
看本地监听端口。-t TCP、-l 只显示监听、-n 不解析服务名、-p 显示进程。
$ ss -tlnp
LISTEN 0 128 0.0.0.0:80 0.0.0.0:* users:(("nginx",pid=2345))
curl
curl url
发送 GET 请求。
curl -X POST -H "Content-Type: application/json" -d '{}' url
发送 POST 请求。
curl -v url
查看完整交互过程——DNS、TCP 握手、TLS 握手、请求头、响应头,调试必用。
curl -w "format" -o /dev/null -s url
测量接口各阶段耗时。一行命令定位瓶颈——网络慢还是服务慢?
$ curl -w "DNS:%{time_namelookup}s 业务处理:%{time_starttransfer}s 总耗时:%{time_total}s\\n" \\
-o /dev/null -s https://api.example.com/endpoint
DNS:0.003s 业务处理:0.234s 总耗时:0.298s
curl -I url
只看响应头。
curl -o file -L url
下载文件并跟随重定向。
包管理
sudo apt install -y nginx
Debian/Ubuntu 系列安装软件。
sudo apt update
刷新软件源缓存(安装前先执行)。
sudo apt remove nginx
卸载软件。
sudo yum install nginx
CentOS/RHEL 系列安装软件。
sudo dpkg -i package.deb
本地安装 .deb 包。
sudo rpm -ivh package.rpm
本地安装 .rpm 包。
七、systemd 与 SSH
systemd——管理服务
systemctl start nginx
启动服务。
systemctl stop nginx
停止服务。
systemctl restart nginx
重启服务。
systemctl reload nginx
重载配置(不中断服务)。
systemctl status nginx
查看服务状态。看 Active 行——active (running) 正常,inactive (dead) 已停止,failed 启动失败。
$ systemctl status nginx
● nginx.service – A high performance web server
Active: active (running) since Sat 2024-06-08 10:00:01 CST
systemctl enable nginx
设置开机自启。
systemctl disable nginx
取消开机自启。
systemd——查看日志
journalctl -u myapp -f -n 100
查看 myapp 服务的日志。-f 实时追踪,-n 100 最近 100 行。
systemd——注册服务
编辑 /etc/systemd/system/myapp.service,将应用注册为系统服务:
[Unit]
Description=我的应用
After=network.target mysqld.service
[Service]
ExecStart=/usr/bin/java -jar /opt/app.jar
Restart=on-failure
RestartSec=10
StartLimitBurst=5
[Install]
WantedBy=multi-user.target
systemctl daemon-reload 重载配置,systemctl enable myapp 开机自启,systemctl start myapp 启动。
定时任务
crontab -e
编辑定时任务。
crontab -l
查看定时任务。
格式:分 时 日 月 周 命令
# 每天凌晨 3 点备份
0 3 * * * /opt/scripts/backup.sh
# 每 10 分钟检查健康状态
*/10 * * * * /opt/scripts/healthcheck.sh
注意 crontab 的环境变量和终端不一样,脚本中用到的 JAVA_HOME 要自己在脚本里设。
SSH
ssh-keygen -t ed25519 -C "you@company.com"
生成密钥对。ed25519 比 RSA 更快更安全。
ssh-copy-id user@server
把公钥复制到服务器,之后就不用输密码了。
ssh myserver
使用 ~/.ssh/config 中配置的别名登录:
Host myserver
HostName 192.168.1.100
User ubuntu
Port 2222
scp file user@host:/path/
安全复制文件到远程。
rsync -avz src/ dst/
增量同步——只传变化的部分。
# 第一次:全量传
$ rsync -avz logs/ backup@nas:/backup/logs/
sent 12.34M bytes
# 第二次(无变化):只传元数据
$ rsync -avz logs/ backup@nas:/backup/logs/
sent 2.34K bytes
ssh -L 3307:127.0.0.1:3306 user@server
本地端口转发——把本地 3307 映射到远程的 3306。然后 mysql -h 127.0.0.1 -P 3307 就能连远程数据库了,全程加密。
八、用户权限与归档压缩
用户管理
sudo useradd -m -s /bin/bash zhangsan
创建用户。-m 创建家目录,-s 指定 shell。
sudo passwd zhangsan
设置密码。
sudo userdel -r zhangsan
删除用户及家目录。
sudo usermod -aG docker zhangsan
把用户追加到 docker 组。-aG 是追加,不是覆盖(漏了 -a 会踢出其他组)。
sudo visudo
配置 /etc/sudoers。必须用 visudo,不能直接编辑,语法错了 sudo 全崩。
zhangsan ALL=(ALL) ALL
%devops ALL=(ALL) ALL
deploy ALL=(ALL) /usr/bin/systemctl restart myapp
归档压缩
tar -czf archive.tar.gz /path/
打包 + gzip 压缩。
tar -xzf archive.tar.gz -C /dir/
解压到指定目录。
tar -tzf archive.tar.gz
查看包内容(不解压)。
tar -cjf archive.tar.bz2 /path/
bzip2 格式——压缩率更高但慢不少。
tar -cJf archive.tar.xz /path/
xz 格式——压缩率最高但最慢。日常用 gzip 就够了。
zip -r archive.zip /path/
zip 压缩。
unzip archive.zip -d /dir/
zip 解压。
环境变量
export JAVA_HOME=/usr/lib/jvm/java-21
设置环境变量(仅当前 shell 有效)。
echo 'export JAVA_HOME=/usr/lib/jvm/java-21' >> ~/.bashrc && source ~/.bashrc
永久设置。~/.bashrc 每次打开终端加载,source 让变化立即生效。
echo $PATH
查看命令搜索路径。
env
查看所有环境变量。
九、综合实战
练习一:日志分析
网站变慢了,从日志里找线索:
# 哪个 IP 访问最多?
$ cat access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head -5
8923 192.168.1.100
56 203.0.113.42
# 每小时请求分布
$ cat access.log | awk '{print $4}' | cut -d' ' -f1 | uniq -c
1203 08/Jun/2024:10
234 08/Jun/2024:04 # 凌晨还有大量请求?
凌晨请求骤增——要么爬虫要么国外用户,有了排查方向。
练习二:系统巡检脚本
#!/bin/bash
echo "=== 系统巡检 $(date) ==="
uptime
free -h | grep Mem | awk '{print "内存已用:" $3 "/" $2}'
df -h | awk 'NR>1 && int($5)>80 {print "⚠️ 磁盘满了:" $1, $5}'
for p in nginx mysql; do
pgrep -x "$p" > /dev/null && echo "✅ $p 运行中" || echo "❌ $p 未运行"
done
$ bash healthcheck.sh
=== 系统巡检 2024-06-08 11:00 ===
11:00 up 2 days, load average: 0.08, 0.12, 0.10
内存已用: 3.4G/7.7G
✅ nginx 运行中
✅ mysql 运行中
练习三:自动备份脚本
#!/bin/bash
BACKUP_DIR="/backup"
SOURCE="/opt/myapp"
TIMESTAMP=$(date +'%Y%m%d')
FILE="myapp_$TIMESTAMP.tar.gz"
tar -czf "$BACKUP_DIR/$FILE" -C "$(dirname $SOURCE)" "$(basename $SOURCE)"
find "$BACKUP_DIR" -name "myapp_*.tar.gz" -mtime +7 -delete
echo "备份完成: $FILE"
# 加到 crontab 每天凌晨自动执行:
0 3 * * * /opt/scripts/backup.sh
写在最后
命令看起来多,但真正每天用的也就二三十个命令。关键在于理解设计思想——“一切皆文件”、管道哲学、“每个命令只做一件事”。
命令行光看没有用,必须自己敲。照着上面在终端里跑一遍,一次记不住没关系,在真实场景里用上两三次就记住了。
加油 🚀




