前两篇讲了文件与文本、进程与网络,把 shell 编程日常打交道的高频命令过了一大半。这一篇是这个系列的最后一部分,讲剩下的三块内容:归档压缩、权限用户、还有一些进阶工具。
这三块命令的特点是"用得不多但每次都要查",归档一年可能就用几次,权限脚本天天用但参数容易忘,xargs 和 cron 这种工具型命令用好了能省很多事。读完这一篇,三篇常用命令系列就完整了。
一、归档与压缩
归档和压缩在 shell 里是两个动作:归档是把多个文件合成一个(用 tar),压缩是把单个文件变小(用 gzip、xz 等)。日常我们用 tar 同时做这两件事------tar 本身不压缩,但可以调用 gzip 或 xz 一起做。
1.1 tar:归档
tar 是 tape archive 的缩写,源自磁带备份时代。现代 tar 的核心用途是把目录打包成一个文件。
bash
# 打包
tar -cf archive.tar dir/
# 解包
tar -xf archive.tar
# 查看内容
tar -tf archive.tar
这几个是最基础的用法。但日常我们更常用"打包+压缩":
bash
# 打包并用 gzip 压缩
tar -czf archive.tar.gz dir/
# 打包并用 xz 压缩(更小但更慢)
tar -cJf archive.tar.xz dir/
# 打包并用 bzip2 压缩
tar -cjf archive.tar.bz2 dir/
# 解压(tar 自动识别压缩格式)
tar -xzf archive.tar.gz
tar -xJf archive.tar.xz
tar -xjf archive.tar.bz2
几个常用选项:
- -c:创建归档
- -x:解压归档
- -t:列出归档内容
- -v:详细输出(列出处理的文件)
- -f:指定文件名(必须紧跟文件名,比如 -f archive.tar)
- -C:解压到指定目录
- --exclude:排除某些文件
- -z:gzip 压缩
- -j:bzip2 压缩
- -J:xz 压缩
- -p:保留权限
几个实战组合:
bash
# 打包并排除某些文件
tar -czf backup.tar.gz --exclude='*.log' --exclude='tmp/' /var/data/
# 打包并显示进度
tar -czf - dir/ | pv > archive.tar.gz
# 解压到指定目录
tar -xzf archive.tar.gz -C /opt/
# 解压单个文件(不解压整个归档)
tar -xzf archive.tar.gz path/inside/file.txt
# 追加文件到现有 tar(不常用)
tar -rf archive.tar newfile.txt
tar 的一个细节 :-f 必须紧跟文件名,不能分开写 -f file.tar。-zcvf 和 -zcfv 等价,但 -zvfc 是不行的。
1.2 各种压缩格式的取舍
python
# gzip:默认选择,平衡速度和压缩率
gzip file.txt # 压缩(会删除原文件)
gunzip file.txt.gz # 解压
# bzip2:压缩率比 gzip 高,速度慢
bzip2 file.txt
bunzip2 file.txt.bz2
# xz:压缩率最高,速度最慢
xz file.txt
unxz file.txt.xz
# zip:跨平台(Windows 也认),压缩率一般
zip -r archive.zip dir/
unzip archive.zip
压缩格式的取舍:
- gzip:最快,压缩率中等,日常首选,
.tar.gz或.tgz后缀。 - bzip2:比 gzip 小一点,比 gzip 慢一点,
.tar.bz2。 - xz:压缩率最高,但慢得多。适合一次压缩、多次解压的场景(比如发布包)。
- zip:跨平台(Windows、macOS 都认),但 shell 脚本里 tar 更主流。
建议日常备份、传输用 gzip,需要发版本包、长期归档用 xz,需要跟 Windows 共享用 zip。
1.3 7z:高压缩率
7z 是 7-Zip 的命令行版本,压缩率比 xz 还高一点。
bash
# 安装
apt install p7zip-full # Debian/Ubuntu
yum install p7zip # CentOS/RHEL
# 压缩
7z a archive.7z dir/
# 解压
7z x archive.7z
7z 适合"必须最小"的场景(嵌入式系统、网络传输受限),日常用得少。
二、权限与用户
权限是 Linux 系统的基础设施。shell 脚本里打交道最多的权限相关命令是 chmod、chown、umask、sudo。这一节把这几个讲清楚。
2.1 文件权限的基础
在讲命令之前,先快速回顾 Linux 文件权限。ls -l 的第一列是权限位:
sql
-rw-r--r-- 1 user group 1024 Jan 1 12:00 file.txt
drwxr-xr-x 2 user group 4096 Jan 1 12:00 dir/
权限位分四组解读:
- 第 1 位:文件类型(
-普通文件、d目录、l链接) - 第 2-4 位:所有者(user)权限
- 第 5-7 位:所属组(group)权限
- 第 8-10 位:其他人(other)权限
每组三位分别是:读 r(4)、写 w(2)、执行 x(1)。数字表示是把三个权限位的值加起来。
2.2 chmod:改权限
bash
# 数字模式
chmod 755 file.sh # rwxr-xr-x
chmod 644 file.txt # rw-r--r--
chmod 600 secret.key # rw-------
chmod 700 private/ # rwx------
# 符号模式
chmod u+x file.sh # 所有者加执行权限
chmod g-w file.txt # 所属组去掉写权限
chmod o=r file.txt # 其他人只设读权限
chmod a+r file.txt # 所有人都加读权限
chmod +x script.sh # 简写:所有人加执行权限
几个常见权限:
- 644:文件默认权限,所有者可读写,其他人只读。
- 755:可执行文件或目录的默认权限,所有人可执行(对目录意味着可进入)。
- 600:私有文件(如密钥),只有所有者能读写。
- 700:私有目录(如 .ssh),只有所有者能进入。
批量改权限:
bash
# 把目录下所有 .sh 加上可执行
find . -name "*.sh" -exec chmod +x {} ;
# 递归设目录为 755,文件为 644(很常见的部署场景)
find /opt/myapp -type d -exec chmod 755 {} ;
find /opt/myapp -type f -exec chmod 644 {} ;
2.3 chown / chgrp:改所有者和组
bash
# 改所有者
chown alice file.txt
# 改所有者和组
chown alice:developers file.txt
# 改组(只改组)
chgrp developers file.txt
# 递归改
chown -R alice:developers /opt/myapp/
chown 只能由 root 执行,普通用户不能改自己文件的所有者,组的话要看用户是不是组成员。
2.4 umask:默认权限
umask 是"用户文件创建掩码"------它决定新创建的文件和目录的默认权限。
bash
# 查看当前 umask
umask
# 输出:0022
# 设置 umask
umask 022
umask 的算法:默认权限减去 umask。
- 文件默认权限是 666
- 目录默认权限是 777
- 实际权限 = 默认权限 - umask
例如 umask 022 时:
- 新文件:666 - 022 = 644
- 新目录:777 - 022 = 755
建议:服务器上 umask 通常是 0022,安全场景用 0077(只有自己能看)。脚本里如果要严格控制文件权限,可以临时改 umask:
bash
old_umask=$(umask)
umask 077
# 接下来创建的文件都是 600 权限
touch secret.key
umask "$old_umask"
2.5 sudo / su:提权
sudo 让普通用户以 root(或别的用户)身份执行命令。
bash
# 以 root 身份执行
sudo apt update
# 以别的用户身份执行
sudo -u alice whoami
# 切换到 root shell
sudo -i
sudo su -
# 编辑文件(需要权限时)
sudo vim /etc/nginx/nginx.conf
# 列出当前用户能 sudo 的命令
sudo -l
sudo 使用时需要注意安全:
- 永远不要
sudo chmod 777------这是给所有人完全权限,生产环境出事故的常见原因 - sudo 不是"万能钥匙"------它受 /etc/sudoers 配置限制,用户必须有权限才能 sudo
- 远程操作重要服务时,避免直接 sudo,改用专门的运维账号
su 是 switch user 的缩写,跟 sudo 区别是:
- sudo 是"借权限",输入自己的密码
- su 是"切换身份",需要目标用户的密码(root 用户切换不需要)
日常推荐用 sudo------可以审计、可配置、不需要共享 root 密码。
2.6 id / groups:查身份
bash
# 当前用户信息
id
# 输出:uid=1000(alice) gid=1000(alice) groups=1000(alice),27(sudo)
# 只看 UID
id -u
# 只看组
id -g
# 列出所有组
groups
id 在脚本里很常用------判断当前是不是 root:
bash
if [ "$(id -u)" -ne 0 ]; then
echo "请用 root 运行此脚本" >&2
exit 1
fi
三、进阶工具
最后这一组是工具型命令,它们本身不直接做事,但能放大其他命令的能力。
3.1 xargs:把 stdin 变成参数
xargs 是 shell 里最容易被低估的命令之一。它解决"命令参数太长怎么办"和"组合命令"两个问题。
bash
# 最常见用法:find + xargs + 命令
find . -name "*.log" | xargs rm
# 等价但更安全(处理文件名含空格的情况)
find . -name "*.log" -print0 | xargs -0 rm
-print0 和 -0 配合用 null 字符分隔------这是处理"文件名含空格"的安全模式。
bash
# 每个文件执行一次命令
echo "file1 file2 file3" | xargs -n 1 cp -- /tmp/
# 并行执行
find . -name "*.jpg" | xargs -P 4 -I {} convert {} -resize 50% {}.small.jpg
-P N 是"用 N 个进程并行执行"------这是 xargs 做并发任务的关键。
bash
# 用占位符指定参数位置
find . -name "*.sh" | xargs -I {} chmod +x {}
# 处理包含特殊字符的输入
echo "name with spaces" | xargs -d '\n' touch
xargs 跟 find -exec 的取舍:
- xargs 性能更好(一次调用处理多个文件),但要小心文件名特殊字符
- -exec 更安全(每个文件单独调用),但慢
默认用 find ... -print0 | xargs -0 ... 模式,又快又安全。
3.2 tee:一箭双雕
tee 把 stdin 同时输出到 stdout 和文件,既能在屏幕看到,又能保存到日志。
bash
# 同时输出到屏幕和文件
ls -la | tee output.log
# 追加(不覆盖)
ls -la | tee -a output.log
# 用 sudo 保存到需要权限的文件
echo "127.0.0.1 example.com" | sudo tee -a /etc/hosts
sudo + tee 的经典用法:
bash
# 把内容写到 root 拥有的文件
echo "新的配置" | sudo tee /etc/myapp.conf >/dev/null
> /dev/null 抑制 tee 自身的输出,只保留写文件的效果。
3.3 crontab:定时任务
crontab 是 Linux 的定时任务工具。语法是 crontab -e 编辑、crontab -l 查看、crontab -r 删除。
cron 的时间格式:分 时 日 月 周 命令
bash
# 每天凌晨 3 点跑
0 3 * * * /opt/backup.sh
# 每 5 分钟跑
*/5 * * * * /opt/monitor.sh
# 每周一上午 9 点
0 9 * * 1 /opt/weekly.sh
# 每月 1 号凌晨
0 0 1 * * /opt/monthly.sh
几个特殊写法:
*:每(每分、每时、每天......)*/N:每 N(每 5 分钟、每 2 小时)1,3,5:1 点、3 点、5 点1-5:1 到 5(连续)
crontab 的几个坑:
- 环境变量不同:crontab 默认的 PATH 很有限,脚本里要用绝对路径
- 输出不会显示:crontab 的输出默认会发邮件,现代服务器没有本地邮件------用
>> /var/log/cron.log 2>&1重定向 - 工作目录不同:crontab 的工作目录是 $HOME,脚本里要 cd 到正确目录
实操建议:写一个 crontab 行时,明确所有路径:
javascript
0 3 * * * /bin/bash /opt/backup.sh >> /var/log/backup.log 2>&1
3.4 screen / tmux:终端复用
screen 和 tmux 是"终端复用器"------让你在一个终端里开多个窗口,断开后能恢复。
tmux 是新一代标准,比 screen 配置更灵活,如果有 tmux 就用 tmux。
bash
# 启动新会话
tmux
# 启动命名会话
tmux new -s mywork
# 列出所有会话
tmux ls
# 重新连接
tmux attach -t mywork
# 在会话里:
# Ctrl+B 然后 c:创建新窗口
# Ctrl+B 然后 n:下一个窗口
# Ctrl+B 然后 p:上一个窗口
# Ctrl+B 然后 d:分离会话(detach)
screen 跟 tmux 类似的快捷键(Ctrl+A 替代 Ctrl+B)。
核心场景 :远程操作时启动一个长期任务,用 screen/tmux 保护它不被断开中断。比如升级服务时 screen -S upgrade,跑完后再 screen -r upgrade 看结果。
3.5 watch:周期执行
watch 每隔一段时间执行一次命令,用来观察变化。
bash
# 每 2 秒看一次磁盘
watch -n 2 df -h
# 每秒看一次进程
watch -n 1 "ps aux | grep nginx"
# 高亮变化的部分
watch -d "ls -la /tmp/"
# 退出码非 0 时停止
watch -e "curl -sf https://api.example.com/health"
watch 是动态监控的好工具,比手动重复命令省事。
3.6 其他值得知道的工具
at :一次性定时任务。at 23:00 进入交互模式,输入命令,Ctrl+D 结束。比 crontab 简单,但用得不多。
script :记录终端会话。script -t timing.log session.log,退出后用 scriptreplay 回放。
timeout :限时执行。timeout 30 long_command,30 秒后发 TERM,超时保护。
bash
# 给命令加超时
timeout 10 curl -s https://slow-api.example.com
# 5 秒后还没下载完就 kill
timeout 5 wget https://example.com/big-file.iso
envsubst :环境变量替换。echo "Hello $NAME" | envsubst 会把 $NAME 替换成环境变量值。
arduino
# 渲染配置模板
envsubst < config.template > config.conf
四、总结
这一篇我们讲完了 shell 常用命令系列的最后一部分------归档、权限、进阶工具。三篇系列合起来,覆盖了日常 95% 的工具场景。
三篇内容回顾:
第一篇讲的是文件操作和文本处理------ls、cp、find、cat、less、head、tail、sort、uniq、cut、paste、tr、join 。
第二篇讲的是进程、网络、系统信息------ps、top、kill、nohup、ping、curl、ssh、scp、rsync、ss、dig、nc、date、free、df 。
第三篇(这一篇)讲的是归档、权限、进阶工具------tar、gzip、chmod、chown、sudo、xargs、tee、crontab、tmux 。
三篇读下来,应该对 shell 日常命令有了完整印象,知道什么时候用哪个、怎么组合。