文件与目录的日常操作是命令行的地基。这一章覆盖查看、复制移动、删除、分页与磁盘占用统计——注意 rmmv、重定向覆盖这些不可逆操作的边界。

cat

cat - 连接文件并输出到标准输出,最常用来查看小文件内容

常用命令

  • cat /etc/hosts 打印文件全部内容(适合短文件;长文件用 less)
  • cat file1.txt file2.txt 依次拼接输出多个文件
  • cat file1.txt file2.txt > merged.txt 把多个文件合并成一个
  • cat -n app.log 输出时给所有行加行号
  • cat -A config.txt 显示不可见字符(Tab 显示为 ^I、行尾为 $,排查空白字符问题)
  • cat -s readme.txt 把连续多个空行压缩成一个
  • cat >> notes.txt 从键盘追加输入到文件,Ctrl-D 结束
  • cat <<'EOF' > run.sh heredoc 写多行内容到文件(脚本里生成配置文件的惯用法)
  • cat - 从标准输入读取(在管道中显式占位)
  • tac app.log | head 逆序查看文件(tac 是 cat 的反向版,GNU;macOS 用 tail -r

语法格式

cat [OPTION]... [FILE]...

FILE 为 - 或省略时读标准输入。

OPTIONS

  • -n 所有行编号
  • -b 只给非空行编号(覆盖 -n)
  • -s 压缩连续空行为一行
  • -A 等价 -vET:显示所有不可见字符(GNU)
  • -E 行尾显示 $
  • -T Tab 显示为 ^I
  • -v^ / M- 记法显示控制字符
  • -e BSD/macOS 下等价 -vE

备注

  • 反模式 UUOC(useless use of cat):cat file | grep foo 直接写 grep foo file 即可;但多文件拼接、heredoc、显式占位管道时 cat 是正解
  • 大文件不要 cat 到终端,用 less 分页或 head/tail 取片段;cat 二进制文件可能弄乱终端,出现乱码后敲 reset 恢复
  • cat a.txt b.txt > a.txt 会先把 a.txt 截断为空再读,导致内容丢失;合并进原文件用 cat b.txt >> a.txt

cp

cp - 复制文件或目录

常用命令

  • cp file.txt /tmp/ 复制文件到目标目录
  • cp file.txt file.bak 复制并改名(快速做备份)
  • cp -r dir/ /backup/dir/ 递归复制整个目录
  • cp -a /etc/nginx /etc/nginx.bak 归档复制:递归且保留权限、属主、时间戳、符号链接(备份配置目录首选)
  • cp -i file.txt /tmp/ 目标已存在时先询问再覆盖
  • cp -n file.txt /tmp/ 目标已存在时不覆盖
  • cp -v *.conf /etc/app/ 复制并逐条打印
  • cp -p script.sh /usr/local/bin/ 保留原文件的权限与时间戳
  • cp -u src/*.js dist/ 只复制比目标新的文件(GNU,增量同步小场景)
  • cp file.txt{,.$(date +%Y%m%d)} 改文件前先做带日期的备份(生成 file.txt.20260714)

语法格式

cp [OPTION]... SOURCE DEST
cp [OPTION]... SOURCE... DIRECTORY

多个 SOURCE 时最后一个参数必须是已存在的目录。

OPTIONS

  • -r / -R 递归复制目录
  • -a 归档模式,等价 -dR --preserve=all:保留链接、权限、时间戳等全部属性
  • -p 保留权限、属主、时间戳
  • -i 覆盖前询问
  • -f 目标无法打开时先删除再重试(强制覆盖)
  • -n 不覆盖已存在的目标
  • -v 显示复制过程
  • -u 仅当源比目标新或目标不存在时才复制(GNU)
  • -L / -P 跟随符号链接复制实体 / 保留符号链接本身

备注

  • 复制目录时源路径结尾斜杠语义不同:GNU 下 cp -r dir /dstcp -r dir/ /dst 都是在 /dst 下生成 dir,而 macOS/BSD 下 dir/ 表示复制目录内容——跨平台脚本想复制内容统一写 dir/.
  • 普通 cp -r 不保留属主和时间戳,备份/迁移用 -a;大量文件或需断点续传时改用 rsync -a
  • cp 覆盖目标是直接截断写入,被覆盖内容不可恢复,重要文件先 -i 或做备份

df

df - 查看文件系统的磁盘空间占用与剩余情况

常用命令

  • df -h 以人类可读单位(K/M/G)显示所有已挂载文件系统的使用情况,最常用
  • df -h / 只查看根分区的空间使用
  • df -h /var/log 查看某个目录所在文件系统的空间(排查日志把盘写满)
  • df -i 查看 inode 使用情况(磁盘还有空间但报 "No space left on device" 时查这个)
  • df -T 同时显示文件系统类型(ext4 / xfs / apfs 等,Linux 用法)
  • df -h -t ext4 只显示指定类型的文件系统
  • df -h -x tmpfs -x devtmpfs 排除 tmpfs 等内存文件系统,只看真实磁盘(Linux)
  • df -h . 查看当前目录所在分区的使用情况
  • df -h | sort -rk 5 | head 按使用率排序,快速找出最满的分区

语法格式

df [OPTION]... [FILE]...

不带参数时列出所有已挂载文件系统;指定 FILE 时显示该文件/目录所在文件系统的信息。

OPTIONS

  • -h 人类可读单位(1K=1024),如 1.5G、200M
  • -H 人类可读单位,但按 1000 进位(1K=1000)
  • -i 显示 inode 使用情况而非块使用情况
  • -T 显示文件系统类型(GNU df)
  • -t <type> 只显示指定类型的文件系统
  • -x <type> 排除指定类型的文件系统
  • -a 显示所有文件系统,包括 0 块的伪文件系统(proc、sysfs 等)
  • -k / -m 以 KB / MB 为单位显示(脚本中取数常用 -k 保证格式稳定)
  • --total 末尾追加一行总计(GNU df)

备注

  • macOS/BSD 的 df 无 -T--total 选项,且默认以 512-byte block 显示,习惯加 -h;查看文件系统类型在 macOS 上用 mountdiskutil list
  • df 统计的是文件系统层面的数据,与 du 逐文件累加的结果可能不一致:文件被删除但仍被进程占用句柄时,df 认为空间未释放而 du 看不到该文件,可用 lsof +L1(Linux)定位
  • 排查磁盘满的套路:先 df -h 定位哪个分区满,再到该分区用 du -sh * 逐层下钻

du

du - 统计文件与目录占用的磁盘空间

常用命令

  • du -sh /var/log 查看目录总占用(最常用组合:s 只显示总计、h 人类可读)
  • du -sh * 查看当前目录下每个文件/子目录的占用,逐层下钻找大文件
  • du -h --max-depth=1 /data 只统计一层子目录的占用(GNU 写法;macOS 用 du -h -d 1
  • du -sh * | sort -rh | head -10 找出当前目录下占用最大的 10 项
  • du -ah /var/log | sort -rh | head -20 连文件一起统计并排出前 20 大
  • du -sh .[!.]* * 连隐藏文件/目录一起统计(家目录排查常用)
  • du -sk * | sort -rn 以 KB 为单位排序,兼容不支持 sort -h 的老系统
  • du -sh --exclude='*.log' /app 统计时排除某类文件(GNU)
  • du -sh -x / 只统计当前文件系统,不跨挂载点(避免把 /proc、其他盘算进来)

语法格式

du [OPTION]... [FILE]...

不指定 FILE 时统计当前目录。默认递归显示每个子目录的占用。

OPTIONS

  • -s 只显示每个参数的总计,不列子目录
  • -h 人类可读单位(K/M/G)
  • -a 连文件一起显示(默认只显示目录)
  • -c 末尾追加一行 total 总计
  • -d N / --max-depth=N 只显示到第 N 层目录(--max-depth 为 GNU 写法,-d 两边通用)
  • -x 不跨文件系统边界(跳过其他挂载点)
  • -k / -m 以 KB / MB 为单位
  • -L 跟随符号链接统计其指向的内容
  • --exclude=PATTERN 排除匹配的文件(GNU du)

备注

  • du 统计的是磁盘占用(按块分配),不是文件逻辑大小;稀疏文件的 du 结果可能远小于 ls -l 看到的大小,加 --apparent-size(GNU)可看逻辑大小
  • macOS/BSD 的 du 不支持 --max-depth--exclude 等长选项,层级控制用 -d N
  • du 与 df 结果对不上时,通常是「已删除但仍被进程持有的文件」在占空间,见 df 笔记

head

head - 输出文件开头部分(默认前 10 行)

常用命令

  • head /var/log/system.log 显示文件前 10 行(默认)
  • head -n 50 app.log 显示前 50 行
  • head -n 1 data.csv 只看第一行(快速确认 CSV 表头/字段顺序)
  • head -c 512 dump.bin | xxd 取前 512 字节并十六进制查看(确认文件真实格式)
  • head -n 5 *.conf 查看多个文件各自的前 5 行,输出带文件名头
  • sort -rn scores.txt | head -3 管道中取 Top 3(head 最常见的用法是配合排序取前 N)
  • head -n -5 app.log 输出除最后 5 行之外的全部内容(GNU,注意负号)
  • ps aux | head -1; ps aux | grep java 先打表头再看目标行的小技巧
  • head -q -n 1 a.csv b.csv 多文件时不打印文件名头,只输出内容

语法格式

head [OPTION]... [FILE]...

省略 FILE 时读标准输入;多个 FILE 时每段前打印 ==> file <== 头。

OPTIONS

  • -n N 显示前 N 行
  • -n -N 显示除末尾 N 行外的全部(GNU 专有)
  • -c N 显示前 N 字节;-c -N 为除末尾 N 字节外全部(GNU)
  • -q 多文件时不打印文件名头
  • -v 总是打印文件名头
  • -N(如 head -20)老式写法,等价 -n 20,不推荐但常见

备注

  • -n -N(去掉末尾 N 行)是 GNU 扩展,macOS/BSD 的 head 不支持,跨平台脚本慎用
  • 与 tail 对照记忆:tail -n +N 是「从第 N 行到末尾」,两者组合可任意切片,如 head -n 100 file | tail -n 1 取第 100 行
  • head 读够 N 行就关闭输入,上游命令会收到 SIGPIPE 提前退出——管道里 set -o pipefail 时可能因此看到非零退出码,属正常现象

less

less - 功能完整的分页器,支持前后滚动与搜索,查看大文件首选

常用命令

  • less app.log 分页查看文件,上下键/PgUp/PgDn 滚动,q 退出
  • less -N app.log 显示行号
  • less +F app.log 进入类似 tail -f 的跟踪模式,Ctrl-C 停止跟踪回到浏览,按 F 恢复跟踪(可搜索可回翻,比 tail -f 好用)
  • less +/ERROR app.log 打开后直接跳到第一个 ERROR
  • less +G app.log 打开后直接跳到文件末尾
  • ps aux | less 给管道输出分页
  • less -S wide.csv 长行不折行、左右方向键横向滚动(看宽表/长 JSON 行)
  • less -i app.log 搜索忽略大小写(搜索词含大写时仍精确匹配)
  • less -R colored.log 保留 ANSI 颜色转义(看 grep --color=always 或彩色日志输出)
  • zless app.log.gz 直接查看 gzip 压缩文件(less 家族配套命令)

语法格式

less [OPTION]... [FILE]...

常用交互按键:Space/b 前后翻页、g/G 跳文件头/尾、/? 向下/向上搜索、n/N 下/上一个匹配、&pattern 只显示匹配行、F 跟踪模式、50g 跳到第 50 行、v 调起编辑器、h 帮助、q 退出。

OPTIONS

  • -N 显示行号
  • -i 搜索忽略大小写(模式含大写字母时自动恢复区分)
  • -S 长行截断不折行,可横向滚动
  • -R 原样输出 ANSI 颜色控制序列
  • -F 内容不足一屏时直接输出并退出
  • -X 退出时不清屏,内容留在终端上
  • -g 搜索时只高亮当前匹配项
  • +N / +G / +/pattern 打开后跳到第 N 行 / 末尾 / 首个匹配
  • -p pattern 等价 +/pattern

备注

  • less 按需读取,不把整个文件载入内存,几个 GB 的日志也能秒开,这是它优于 cat/编辑器的核心场景
  • git logman 等默认用 less 分页,行为可通过环境变量 LESS 定制,常见配置 export LESS='-iRFXS'
  • less +F 基本可替代 tail -f:跟踪中随时 Ctrl-C 停下来搜索回翻;缺点是对被轮转的文件不会像 tail -F 那样自动重开

more

more - 按页显示文件内容的分页器(功能较少,几乎所有系统都自带)

常用命令

  • more app.log 分页查看文件,空格翻页、q 退出
  • ps aux | more 给管道输出分页
  • more +100 app.log 从第 100 行开始显示
  • more +/ERROR app.log 打开后直接定位到第一个匹配 ERROR 的位置
  • more -d app.log 底部显示操作提示(按错键时给出帮助,适合不熟的人)
  • more -s app.log 压缩连续空行为一行
  • more -10 app.log 每屏只显示 10 行

语法格式

more [OPTION]... FILE...

交互按键:Space 下翻一屏、Enter 下翻一行、/pattern 向下搜索、n 下一个匹配、= 显示当前行号、v 调起编辑器、q 退出。

OPTIONS

  • -d 显示操作提示信息,按错键不响铃而是给提示
  • -s 压缩连续空行
  • -f 按逻辑行计数而非折行后的屏幕行
  • -p 不滚动,先清屏再逐页显示
  • -c 从顶部开始逐行覆盖刷新,不滚屏
  • -u 抑制下划线(老终端兼容)
  • -N(如 -10)设置每屏显示的行数
  • +N 从第 N 行开始显示
  • +/pattern 从匹配 pattern 处开始显示

备注

  • more 只能向前翻(部分新版实现支持有限回退),不能随意上下滚动;日常查看优先用 less,more 的价值在于极简环境(容器、恢复模式)里一定存在
  • more 读完文件末尾会自动退出(less 默认停在末尾等待),管道场景想看完自动结束时 more 反而顺手
  • macOS 上 more 实际由 less 提供(同一二进制),行为与传统 more 略有差异

mv

mv - 移动文件/目录,或对其重命名

常用命令

  • mv old.txt new.txt 重命名文件
  • mv file.txt /tmp/ 把文件移动到目标目录(保留原名)
  • mv file1.txt file2.txt dir/ /backup/ 把多个文件/目录一起移动到最后一个参数指定的目录
  • mv -i file.txt /tmp/ 目标已存在时先询问是否覆盖(交互式,防误覆盖)
  • mv -n file.txt /tmp/ 目标已存在时不覆盖、直接跳过
  • mv -v *.log /var/log/archive/ 移动并逐条打印做了什么
  • mv dir1 dir2 若 dir2 不存在则重命名为 dir2;若 dir2 已存在则把 dir1 移入 dir2 下
  • mv -- -weird-name.txt normal.txt-- 处理以 - 开头的文件名
  • mv ~/Downloads/report{_draft,}.md 配合 shell 花括号展开快速改名(等价于 mv report_draft.md report.md)

语法格式

mv [OPTION]... SOURCE DEST
mv [OPTION]... SOURCE... DIRECTORY

两个参数且 DEST 不是已存在目录时为重命名;最后一个参数是已存在目录时为移动(可多源)。

OPTIONS

  • -i 覆盖前询问确认
  • -f 强制覆盖,不询问(默认行为,主要用于压掉别名里的 -i)
  • -n 不覆盖已存在的目标文件
  • -v 显示移动过程
  • -b 覆盖前给目标文件做备份(GNU,生成 ~ 后缀备份)
  • -u 仅当源文件比目标新(或目标不存在)时才移动(GNU)
  • -t DIR 指定目标目录,源文件放后面(GNU,方便配合 xargs)
  • -- 选项结束标记,其后参数一律视为文件名

备注

  • 同一文件系统内 mv 只改目录项(rename),瞬间完成;跨文件系统时实际是「复制 + 删除」,大文件会慢且中断后可能留下不完整副本
  • -f-i-n 同时出现时以最后一个为准;很多系统给 root 配了 alias mv='mv -i',脚本里想强制覆盖要显式 -f 或用 command mv
  • mv 目录时注意目标是否已存在:mv dir existing 当 existing 是已存在目录时是把 dir 移进它里面,与重命名行为不同,操作前先确认

rm

rm - 删除文件或目录(不可恢复)

常用命令

  • rm file.txt 删除单个文件
  • rm -i file.txt 删除前逐个询问确认
  • rm -f file.txt 强制删除:忽略不存在的文件、不询问(脚本中常用)
  • rm -r dir/ 递归删除目录及其全部内容
  • rm -rf /tmp/build/ 强制递归删除,最常用也最危险的组合,敲回车前先确认路径
  • rm -v *.tmp 删除并逐条打印删了什么
  • rm -- -file.txt 删除以 - 开头的文件(或用 rm ./-file.txt
  • find . -name '*.log' -mtime +30 -delete 批量删除 30 天前的日志(比 rm $(find ...) 安全,不受文件名空格影响)
  • rm -rI big_dir/ 删除超过 3 个文件时只询问一次(GNU,比 -i 省事又有兜底)

语法格式

rm [OPTION]... FILE...

删除目录必须加 -r(或 -d 删空目录)。

OPTIONS

  • -f 强制删除,忽略不存在的文件,从不询问
  • -i 每个文件删除前询问
  • -I 删除超过 3 个文件或递归删除时询问一次(GNU)
  • -r / -R 递归删除目录
  • -d 删除空目录(等价 rmdir)
  • -v 显示删除过程
  • --preserve-root 拒绝对 / 递归删除(GNU 默认开启)
  • -- 选项结束标记,防止文件名被当成选项

备注

  • rm 删除是解除目录项链接,没有回收站;误删后立即停止对该分区写入,恢复概率才高。日常可考虑用 trash 类工具替代交互式删除
  • 变量拼路径时务必防空值:rm -rf "$DIR"/ 若 $DIR 为空会变成 rm -rf /。脚本里用 rm -rf "${DIR:?}"/ 让空值直接报错退出
  • macOS/BSD rm 无 -I--preserve-root;另外文件仍被进程打开时删除不会立刻释放磁盘空间(见 df 笔记)

tail

tail - 输出文件末尾部分,最常用来追日志

常用命令

  • tail /var/log/system.log 显示文件最后 10 行(默认)
  • tail -n 100 app.log 显示最后 100 行
  • tail -f /var/log/system.log 实时跟踪日志新增内容,Ctrl-C 退出
  • tail -F app.log 跟踪且容忍文件被轮转/重建(按文件名重新打开,盯 logrotate 的日志用这个)
  • tail -n 200 -f app.log 先显示最后 200 行再继续跟踪
  • tail -f app.log | grep --line-buffered ERROR 实时过滤错误日志
  • tail -n +2 data.csv 从第 2 行开始输出到末尾(跳过 CSV 表头,注意是 +
  • tail -f access.log error.log 同时跟踪多个文件,输出带文件名分隔头
  • tail -c 1024 dump.bin 显示最后 1024 字节
  • tail --pid=1234 -f build.log 跟踪到指定进程退出后自动停止(GNU)

语法格式

tail [OPTION]... [FILE]...

多个 FILE 时每段输出前会打印 ==> file <== 头。

OPTIONS

  • -n N 显示最后 N 行;-n +N 表示从第 N 行开始直到末尾
  • -c N 显示最后 N 字节;同样支持 +N
  • -f 跟踪文件追加内容(follow by descriptor)
  • -F 等价 -f --retry,按文件名跟踪,文件被轮转后自动重新打开
  • -q 多文件时不打印文件名头
  • -v 总是打印文件名头
  • -s N 配合 -f,设置轮询间隔秒数
  • --pid=PID 配合 -f,PID 进程结束后停止跟踪(GNU)

备注

  • -f-F 的区别是排障关键:-f 盯住文件描述符,日志被 logrotate 挪走后就再也读不到新内容;-F 会按路径重开新文件。追长期运行的服务日志一律用 -F
  • 老写法 tail -100 file 在 GNU 下仍可用但已不推荐,POSIX 标准写法是 -n 100
  • 管道中 -f 后接 grep/awk 要注意缓冲:grep 加 --line-buffered,awk 用 fflush(),否则输出会攒着不出来

touch

touch - 创建空文件,或更新已有文件的访问/修改时间戳

常用命令

  • touch newfile.txt 文件不存在则创建空文件;已存在则把时间戳更新为当前时间
  • touch a.txt b.txt c.txt 一次创建/更新多个文件
  • touch file{1..5}.txt 配合 shell 展开批量创建 file1.txt ~ file5.txt
  • touch -c file.txt 只更新时间戳,文件不存在时创建
  • touch -m file.txt 只更新修改时间(mtime)
  • touch -a file.txt 只更新访问时间(atime)
  • touch -t 202601011200 file.txt 把时间戳设为指定时间(格式 [CC]YYMMDDhhmm[.ss])
  • touch -d '2026-07-01 09:00' file.txt 用日期字符串设置时间(GNU)
  • touch -r ref.txt file.txt 把 file.txt 的时间戳设置成与 ref.txt 相同

语法格式

touch [OPTION]... FILE...

OPTIONS

  • -a 只改访问时间 atime
  • -m 只改修改时间 mtime
  • -c / --no-create 文件不存在时不创建
  • -t STAMP 使用 [[CC]YY]MMDDhhmm[.ss] 格式指定时间
  • -d STRING 使用日期字符串指定时间(GNU;BSD 语义不同)
  • -r FILE 参照另一文件的时间戳
  • -h 作用于符号链接本身而非其指向的文件

备注

  • touch 常见的两大用途:占位创建空文件;配合 make 等基于 mtime 的工具强制触发/跳过重建
  • 更新他人文件的时间戳需要对该文件有写权限;任意指定时间戳(-t/-d/-r)需要是文件属主或 root
  • 很多 Linux 挂载默认 relatime,atime 并非每次读取都更新,用 atime 做「最近访问」判断要留意挂载选项