文件系统、权限与文本处理

共 73 题
📑 题目列表 73 题
#
★★★

1. /etc/fstab 写错导致系统无法启动的恢复路径,救援模式下如何只读修复

/etc/fstab 配置错误(如设备名、挂载参数、UUID 写错)导致系统无法启动时,如何进入救援模式并只读修复 fstab?

  • 系统启动挂载根目录与 fstab 的处理顺序
  • 救援模式(rescue/emergency target)的进入方式
  • 只读挂载与修复的注意事项

fstab 写错常见的表现是启动时报"Failed to mount"或"you are in emergency mode",此时系统会进入 emergency/rescue。恢复路径:一是在 grub 引导时按 e 编辑内核参数,在 linux 行末尾追加 systemd.unit=rescue.targetsingle,进入单用户/救援模式;二是若 root 分区以只读挂载,先 mount -o remount,rw / 重新挂载为可写;三是用 blkidlsblk -f 查询正确 UUID,修正 /etc/fstab 中错误行;四是移除错误行或改回正确配置后 mount -a 验证全部挂载项;五是 sync 后重启。注意救援模式下默认只读,避免写坏文件系统,务必先确认根分区可写再修改。

启动失败本质是 fstab 中某个挂载项无法满足,systemd 触发 emergency。关键是"进救援 + 只读转 rw + 用 blkid 核实 UUID + mount -a 验证"。fstab 中应优先用 UUID 而非设备名(/dev/sda 会因设备枚举变化而失效)。

# 救援模式下查看并修正 fstab
mount -o remount,rw /
blkid /dev/sda1
grep -v '^#' /etc/fstab
mount -a          # 验证所有挂载项
#
★★★

2. /proc/sys/fs/file-max 与 /proc/sys/fs/nr_open 在 fd 限制的层级关系

/proc/sys/fs/file-max 与 /proc/sys/fs/nr_open 都是文件描述符上限,二者在 fd 限制的层级关系是怎样的?

  • file-max 与 nr_open 的语义
  • 系统级、进程级(ulimit)与单进程上限的层级
  • 修改与生效约束

file-max 是系统范围内所有进程可打开文件句柄的总上限(硬上限,内核分配超过即报错),nr_open 是单个进程可打开文件的硬上限(不能超过该值)。层级关系为:系统级 file-max 约束系统全部 fd 总量;进程级受 ulimit -n(soft/hard)约束;而 nr_open 是单进程 fd 的绝对上限,任何进程的 fd 数都不能超过 nr_open。因此要提升单进程 fd,需同时满足:ulimit -n 提高、不超过 nr_open、且系统中所有进程 fd 总和不超过 file-max。修改 file-max 用 sysctl fs.file-max,nr_open 用 sysctl fs.nr_open,nr_open 一般应大于或等于预期的单进程上限。

关键区分"总量 vs 单进程上限"。file-max 管总盘子,nr_open 管单进程天花板,ulimit 管进程软硬限制。排查 Too many open files 时需三级都要看。

sysctl fs.file-max fs.nr_open
ulimit -n
# 提升单进程 fd 上限
sysctl -w fs.nr_open=1048576
ulimit -n 1048576
#
★★★

3. /proc、/sys、/dev 三个虚拟文件系统分别承担什么职责

/proc、/sys、/dev 三个虚拟文件系统各自承担什么职责,在运维中如何区分使用?

  • proc 的进程与内核信息
  • sysfs 的设备和内核对象
  • devtmpfs 的设备节点

三个都是虚拟文件系统(不占磁盘)。/proc 存放进程与内核运行时信息:/proc/ 进程信息、/proc/meminfo、/proc/cpuinfo、/proc/net 等,用于查看与调整内核参数(/proc/sys)。/sys(sysfs)暴露内核设备与驱动对象,以树状结构描述设备、总线、驱动、模块,如 /sys/block、/sys/class/net,用于设备与供电等调试。 /dev(devtmpfs)由内核动态创建设备节点文件(/dev/sda、/dev/null 等),是用户空间访问设备的中介。职责上:proc 侧重"进程与运行时内核状态",sys 侧重"设备与驱动模型",dev 侧重"设备节点本身"。运维排障时查进程用 /proc,查设备特性用 /sys,操作设备用 /dev。

注意三者虽都叫虚拟文件系统但分工侧重不同。proc 是进程/内核信息接口,sysfs 是设备拓扑接口,devtmpfs 是设备节点。

ls /proc/$$/            # 当前进程信息
cat /proc/meminfo
ls /sys/class/net/      # 网络设备
ls /dev/sd*             # 磁盘设备节点
#
★★★

4. LVM snapshot 与文件系统 snapshot 在备份场景的差异

LVM snapshot 与文件系统原生 snapshot(如 ext4/xfs/btrfs)在备份场景上有哪些差异,如何选择?

  • LVM snapshot 的基于 COW 的实现
  • 文件系统级 snapshot 的机制
  • 备份一致性、容量与恢复

LVM snapshot 基于 copy-on-write(COW):创建快照时记录原卷的元数据,当原卷被修改时,把旧数据块复制到快照的预留空间,因此快照本身占用的空间随数据变化增长,且快照需要预留空间(overprovisioning),空间不足会失效。文件系统级 snapshot(如 btrfs subvolume snapshot、xfs 的 xfs_freeze + 复制的文件系统一致性视图)由文件系统管理,能保证文件系统内部一致性(如日志、元数据),btrfs 快照几乎是零成本的元数据快照。差异:LVM snapshot 工作在块层,对上层文件系统透明,但 COW 快照存在性能与空间问题;文件系统 snapshot 更贴近文件系统语义,一致性更好,但依赖具体文件系统特性。备份场景:追求崩溃一致性且文件系统支持时优先文件系统 snapshot;需要跨多个文件系统/分区统一的块级快照时用 LVM。无论哪种,数据库等需要应用一致性的场景仍需配合应用层 flush/一致性点。

核心差异是"块层 COW vs 文件系统层一致性"。LVM 透明但需预留空间且 COW 性能衰减,文件系统 snapshot 更省空间、一致性更好但依赖 FS 能力。

# LVM 快照
lvcreate -L 10G -s -n snap /dev/vg/data
mount -o ro /dev/vg/snap /mnt/snap
# btrfs 子卷快照
btrfs subvolume snapshot /mnt/data /mnt/.snap/20260804
#
★★★

5. Linux ACL 与 POSIX 权限的兼容性与冲突处理

Linux ACL 与标准 POSIX rwx 权限如何兼容,二者冲突时如何处理?

  • POSIX 基本权限与 ACL 的映射
  • ACL mask 的语义
  • 冲突处理与工具

ACL(Access Control List)在标准 POSIX 权限之上扩展了多用户/多组授权。三组基本权限(owner、group、other)是 ACL 的子集:ACL 中的 user 条目、group 条目与 mask 对应。核心机制是 mask:当设置命名用户/命名组 ACL 时,mask 被自动计算为"现有 group 位与命名条目权限的最大值",mask 限制所有 named user/group 以及 group 位的最大有效权限,但不限制 owner 与 other。因此 ACL 与 POSIX 权限"兼容"体现在 getfacl 输出的 mask 与 ls -l 的 group 位关联。冲突处理:当传统权限(chmod)与 ACL 同时存在时,chmod 会更新 mask 值,可能改变命名条目的有效权限(显示为 mask 的权限)。用 getfacl/setfacl 查看与修改,setfacl -b 可清除 ACL 回到纯 POSIX。

ACL 是 POSIX 权限的超集,mask 是两者衔接的关键。运维常见坑是 chmod 后命名用户权限"失效",实为 mask 收窄,需 setfacl -m m::rwx 调整 mask。

setfacl -m u:alice:rwx /data      # 给单个用户授权
setfacl -m g:devs:r-x /data
getfacl /data
setfacl -b /data                  # 清除 ACL
#
★★★

6. Linux 中 df -i 与 df -h 输出不一致时如何判断根因

df -i 与 df -h 输出不一致(一个满一个不满)时,如何判断根因?

  • df -h 看块使用,df -i 看 inode 使用
  • 常见根因:inode 耗尽 vs 空间耗尽
  • 排查手段

df -h 显示文件系统块的空间使用率,df -i 显示 inode 使用率。两者不一致说明:若 df -i 显示 100% 而 df -h 远未满,说明 inode 耗尽——文件系统里创建了海量小文件,占满了 inode 表,即使空间还有也无法新建文件/目录;若 df -h 满而 df -i 未满,则是空间耗尽,通常是日志、大文件、回收站等占满。判断根因:用 df -hdf -i 对比;find / -xdev -type f | wc -l 统计 inode 分布;du -xh --max-depth=1 查空间占用。处理:inode 耗尽要清理小文件(如 /tmp、缓存目录、邮件队列),或重建(mkfs 时加大 inode 数,如 ext4 的 -i bytes-per-inode);空间满则清理大文件或用 lsof 查已删未释放句柄。

核心是"inode 稀缺 vs 空间稀缺两类问题"。inode 耗尽常因海量小文件(如 docker 镜像层、缓存)导致,空间满则多为日志/大文件。df 只是表象,需找具体目录。

df -h
df -i
# 找 inode 占用最多的目录
for d in /*; do echo "$d $(find "$d" -xdev -type f 2>/dev/null | wc -l)"; done
du -xh --max-depth=1 / | sort -rh | head
#
★★★

7. Linux 中 inode 耗尽、文件描述符耗尽、磁盘配额满三类故障如何快速区分

inode 耗尽、文件描述符耗尽、磁盘配额满三类故障表现相似却根因不同,如何快速区分?

  • 三类故障的报错表现
  • 对应命令与判定
  • 处理方式

三类故障都表现为"无法创建/写入文件",但报错与根因不同。inode 耗尽:报"No space left on device",但 df -h 空间充足、df -i 100%,根因是海量小文件占满 inode。文件描述符耗尽:报"Too many open files",是进程级 fd 达到上限(ulimit -n 或 file-max),根因是进程泄漏句柄或并发过高,用 ulimit -nlsof -p <pid> | wc -l/proc/<pid>/fd 判断。磁盘配额满:报"Disk quota exceeded",是用户/组超出配额(quota),df 显示空间正常但特定用户无法写入,用 quota -urepquota 判断。快速区分:先看报错关键字(No space / Too many open files / Disk quota exceeded),再看 df -h、df -i、ulimit、quota 四项。

区分要点是"报错关键字 + 空间/索引/进程/配额四个维度"。运维应建立快速判定流程,避免把 inode 耗尽误判为磁盘满而误删数据。

df -h; df -i
ulimit -n; lsof -p <pid> | wc -l
quota -u <user>; repquota -a
#
★★★

8. Linux 文件权限 rwx 三组分别控制什么,目录的 x 位为何与读文件不同

Linux 文件权限 rwx 三组分别控制什么?为什么目录的 x(执行)位与读文件时的 x 位含义不同?

  • rwx 对文件与目录的不同语义
  • 目录 x 位(进入/搜索)与文件 x 位(执行)的区别
  • 目录 r 与 x 的配合

rwx 三组分别对应 owner、group、other 三类主体的权限。对普通文件:r 读、w 写、x 执行。对目录:r 可列出目录内容(ls 可见文件名),w 可在目录内创建/删除/重命名文件,x 可进入目录(cd)并访问其中文件(traverse/search),x 位是目录的"可通行"权限。目录的 x 位与文件的 x 位含义不同:文件的 x 表示"可执行",目录的 x 表示"可进入/可搜索",即使没有某文件的读权限,只要有目录的 x 位也能知道文件存在并可访问(取决于文件自身权限)。实际中目录仅有 r 而无 x 时,ls 能看到文件名但无法 stat 其属性(会报错),因此目录通常需要 r 与 x 配合才能正常浏览。删除文件需要目录的 w+x 权限,而不是文件自身的写权限。

关键理解"目录权限控制的是对目录内目录项的访问而非文件内容"。文件的 x 表执行,目录的 x 表进入/搜索,二者语义不同。这也是常考细节。

ls -ld /data          # 查看目录权限
chmod 755 /data        # rwxr-xr-x:owner 可读写进,其余可进可读
# 目录无 x 时无法进入
chmod 644 /data; cd /data  # Permission denied
#
★★★

9. chmod 777 在生产环境为何是高危操作,何时只能用于临时调试

chmod 777 在生产环境为何是高危操作?何时才允许临时使用?

  • 777 的权限含义与风险
  • 提权与篡改风险
  • 临时调试的边界

chmod 777 使所有用户(owner/group/other)都可读、可写、可执行,对文件意味着任何用户都能修改内容(包括可执行文件被篡改),对目录意味着任何用户都能在里面创建/删除/重命名文件。生产风险:一是任意用户可篡改程序/配置/脚本,造成恶意代码注入或数据破坏;二是可写的目录配合可执行文件可能导致提权(如把恶意二进制放入 PATH 目录);三是破坏最小权限原则,审计与被入侵后的责任难界定。因此生产环境应避免 777,改用最小必要权限(如 755/750/700)或 ACL。临时调试场景:仅当确需共享写权限且无更优方案时(如临时检查应用为何无法写入、演示环境、一次性容器),且应尽快收紧,并记录变更。正确做法是定位具体用户/组用 ACL 或属组授权,而不是无差别 777。

根本风险是"打破最小权限 + 可被任意篡改/提权"。777 是"权限治理的失败",应通过属组 + ACL 精确定位,仅在临时排障时短暂使用并回收。

# 高危:所有用户可写可执行
chmod 777 /data
# 更安全:属组写
chown :app /data && chmod 775 /data
# 临时调试后立即收紧
chmod 755 /data
#
★★★

10. chown、chgrp、umask 三者如何配合确保新建文件的默认权限安全

chown、chgrp、umask 三者如何配合,以确保新建文件的默认权限安全?

  • chown/chgrp 设置属主与属组
  • umask 决定默认权限屏蔽
  • 三者协同的实践

chown 修改文件属主,chgrp 修改文件属组,二者决定"文件归谁、谁享有属组权限";umask 决定新建文件/目录的默认权限(对 666/777 进行屏蔽)。三者配合确保安全:一是用 chown 把属主设为正确的应用账号,用 chgrp 把属组设为需要共享的组,从而避免用 777 共享;二是设置合适的 umask(如 022 默认 644/755,027 更严格,077 更紧),使新建文件默认不给其他用户写权限;三是通过属组 + 目录组权限(如 setgid 目录)让同组用户共享写,而不是开放给所有用户。例如:目录属主 root:app,chmod 2770,chown 到 app 组,配合 umask 007,则 app 组成员可读写且其他用户不可见。关键是把"共享"限定在属组范围内,而非全局。

核心是"精确授权 + 默认收紧"。chown/chgrp 定归属,umask 定默认门槛,配合 setgid 目录让同组共享,避免 777 与全局开放。

chown app:app /data
chgrp app /data
chmod 2770 /data          # setgid + 组读写给
umask 007                 # 新建文件默认仅属主/属组可读写
#
★★★

11. fdisk、gdisk、parted 在 MBR 与 GPT 上的差异

fdisk、gdisk、parted 三个分区工具在 MBR 与 GPT 分区表上的能力差异是什么?

  • MBR 与 GPT 的格式差异
  • 三个工具对 MBR/GPT 的支持
  • 选型

MBR 分区表最大支持 2TB 磁盘、最多 4 个主分区(或扩展分区内多逻辑分区),用 32 位 LBA 地址;GPT 支持 2TB 以上磁盘、最多 128 个分区(可更多)、用 64 位 LBA,包含 CRC 校验与备份分区表,兼容性更好。工具差异:fdisk 传统上支持 MBR,现代 util-linux 的 fdisk 也支持 GPT(自动识别),但历史上以 MBR 为主;gdisk(GPT fdisk)专门操作 GPT,支持 GPT 的完整特性(如分区名、GUID);parted 同时支持 MBR 与 GPT,可交互或脚本化(parted -s),适合非交互与自动化,且能调整分区大小。选型:>2TB 或需多分区用 GPT,可用 gdisk 或 parted;脚本化自动化用 parted -s;交互精细操作用 fdisk/gdisk。现代 fdisk 已能处理 GPT,但 gdisk 对 GPT 的校验与恢复更专业。

核心是"磁盘大小与分区表格式决定工具"。>2TB 必须 GPT,自动化用 parted,交互用 fdisk/gdisk。注意 parted 与 fdisk 的单位换算(MB/MiB)差异。

# GPT 分区(parted 脚本化)
parted -s /dev/sdb mklabel gpt
parted -s /dev/sdb mkpart primary 1MiB 100%
# 查看分区表
gdisk -l /dev/sdb
fdisk -l /dev/sdb
#
★★★

12. find 与 xargs、find -exec 的区别,删除百万级文件时如何避免参数过长

find 与 xargs、find -exec 在处理文件时的区别是什么?删除百万级文件时如何避免参数过长(Argument list too long)?

  • find -exec 逐条执行 vs xargs 批量传递
  • ARG_MAX 与参数过长问题
  • xargs -0/-n 与分批删除

find 本身只负责查找,-exec 是对每个匹配文件执行命令:find . -name '*.log' -exec rm {} \; 每条命令执行一次(性能差),-exec ... + 则会批量追加参数(类似 xargs)。xargs 把 find 输出的参数按批次传给命令,find ... | xargs rm 可分批处理,配合 -n 指定每批条数、-0 处理含空格/换行的文件名、-P 并行。删除百万级文件时问题:rm *.log 因参数超出 ARG_MAX 报"Argument list too long";且 find 输出超长时 xargs 默认按 ARG_MAX 分批,但若不处理特殊字符会出错。安全做法:find <dir> -type f -name '*.log' -print0 | xargs -0 -r rm -f,或 find ... -delete(find 内建删除,无需外部命令,最省内存)。对海量文件的目录,直接 rm -rf 也慢,可用 rsync --delete 空目录对拷。

核心是"逐条 vs 批量 vs 内建"。-exec; 逐条慢,-exec+ 与 xargs 批量,-delete 内建最省资源。百万级文件务必用 -print0 + xargs -0 或 -delete 避免参数与文件名问题。

find /data -type f -name '*.log' -delete
find /data -type f -print0 | xargs -0 -n 1000 rm -f
find /data -type f -exec rm -f {} +   # 批量
#
★★★

13. grep、egrep、fgrep、固定字符串与 PCRE 在性能与可维护性上的取舍

grep、egrep、fgrep 的区别,以及固定字符串匹配与 PCRE 正则在不同场景下的性能与可维护性取舍是什么?

  • grep/egrep/fgrep 的正则级别
  • 固定字符串 vs 正则的性能与精确性
  • 可维护性考量

grep 默认使用基础正则(BRE,基本元字符),egrep(grep -E)使用扩展正则(ERE,支持 + ? | 等),fgrep(grep -F)把模式当作固定字符串完全匹配,不做正则解释。性能上:fgrep 固定字符串匹配最快(无正则回溯开销),适合大量字面量匹配(如日志中的具体 IP、关键字);egrep 的 ERE 比 BRE 更易读、更少转义;grep -P 使用 PCRE(Perl 正则),功能最强大但性能最差(回溯、复杂正则可能指数级)。可维护性:固定字符串简洁、无歧义、易审查;正则灵活但复杂正则难读难维护。取舍:能精确匹配字面量就用 -F(性能、明确);需要简单结构用 -E;需要复杂前瞻/回溯才用 -P,并注意性能与安全(避免 ReDoS)。生产日志分析常组合 -F 加快首次筛选再 -E 细化。

核心是"匹配能力与性能的权衡"。固定字符串最稳最快,ERE 平衡,PCRE 最强但最贵。选型看需求:字面量用 -F,结构化用 -E,复杂才用 -P。

grep -F '10.0.0.1' app.log          # 固定字符串,最快
grep -E 'error|fatal' app.log       # 扩展正则
grep -P '(\d{1,3}\.){3}\d{1,3}' app.log  # PCRE
#
★★★

14. mount 的 noexec、nosuid、nodev 三类安全挂载选项分别在哪些场景启用

mount 的 noexec、nosuid、nodev 三个安全挂载选项分别适用于哪些场景?

  • 三个选项的语义
  • 各选项的适用挂载点
  • 加固实践

noexec 禁止从该挂载点执行二进制文件(防止从可写目录运行恶意程序);nosuid 禁止该文件系统上的 setuid/setgid 位生效(防止提权);nodev 禁止在该文件系统上创建/访问设备节点(防止伪造设备)。适用场景:/tmp、/var/tmp、/dev/shm 等用户可写目录应挂 noexec,nosuid,nodev(防止执行恶意脚本、提权、伪造设备);nosuid 应应用于所有用户可写且无必要 setuid 的挂载点;nodev 适用于 /home、/var 等非设备目录;/dev 等真正需要设备节点的地方不能 nodev。但注意:有些应用依赖 /tmp 可执行(如某些安装器、临时构建),需评估后决定是否启用 noexec。生产加固基线通常对 /tmp、/dev/shm、/var/tmp 施加三类选项。

核心是"在用户可写且无必要特殊权限的挂载点上收紧"。对应关系:noexec 防执行、nosuid 防提权、nodev 防伪设备,三者常组合用于 /tmp /dev/shm 等。

# 加固 /tmp
mount -o remount,noexec,nosuid,nodev /tmp
# fstab 中配置
echo "tmpfs /dev/shm tmpfs defaults,noexec,nosuid,nodev 0 0" >> /etc/fstab
#
★★★

15. sed 与 awk 在字段切分、流式处理与状态机场景下的选型差异

sed 与 awk 在字段切分、流式处理与状态机处理场景下如何选型?

  • sed 的流式编辑与行处理
  • awk 的字段切分与状态处理
  • 选型原则

sed 是流编辑器,擅长按行做替换、删除、插入、打印等编辑操作,模式是基于行与正则的简单替换,不适合复杂字段处理与多行状态。awk 是文本处理语言,自动按分隔符(FS)切分字段($1、$2...),支持变量、数组、循环、条件与"模式-动作",擅长字段提取、统计、汇总、跨行状态机(如处理段落、累计计数)。选型:仅做行级替换/删除用 sed(如 sed -i 's/old/new/g');需要按字段切分、聚合统计、依赖行间状态(如累加、聚合、多行合并)用 awk。sed 处理"单行模式"轻量,awk 处理"结构化/状态"更强大。二者常配合:sed 做清洗,awk 做分析。可维护性上 awk 脚本比 sed 的复杂正则更易读。

核心是"sed 行编辑 vs awk 字段/状态处理"。sed 适合简单行替换,awk 适合字段切分、统计与状态机;复杂结构优先 awk。

# sed 行级替换
sed -i 's/error/ERROR/g' app.log
# awk 字段切分与统计
awk '{print $1, $NF}' access.log
awk '{sum+=$3} END{print sum}' stats.txt
#
★★★

16. setuid、setgid、sticky bit 三类特殊位分别在哪些场景使用

setuid、setgid、sticky bit 三类特殊权限位分别适用于哪些场景?

  • setuid 与提权
  • setgid 与目录/文件共享
  • sticky bit 与目录保护

setuid(s 在 owner 位,如 4755):文件以属主身份运行,常用于让普通用户执行需要 root 权限的程序(如 passwd、sudo),但因其提权风险,生产应尽量减少 setuid 二进制。setgid(s 在 group 位,如 2755):文件以属组身份运行,目录的 setgid 让新建文件继承目录属组,常用于共享目录让同组成员自动归属同一组。sticky bit(t 在 other 位,如 1777):目录内只有 owner、root 或目录 owner 才能删除/重命名自己的文件,典型是 /tmp(1777),防止用户删除他人文件。适用场景:setuid 用于少数确需提权的系统命令;setgid 用于共享工作目录(配合属组写入);sticky bit 用于公共可写目录防止互相删除。find / -perm -4000 可审计 setuid 文件。

核心是"三个位分别解决提权、组共享、公共目录保护"。setuid 提升执行者权限,setgid 目录继承属组,sticky 保护公共目录内文件不被删除。

chmod 4755 /usr/bin/foo   # setuid
chmod 2770 /shared        # setgid 目录
chmod 1777 /tmp           # sticky bit
find / -perm -4000 -type f   # 审计 setuid
#
★★★

17. umask 计算默认权限时为何对目录与文件行为不同

umask 计算默认权限时,为什么对目录与文件的行为不同?

  • 文件和目录的默认权限基准
  • umask 屏蔽逻辑
  • 为何文件不默认给执行位

umask 指定新建文件/目录时被屏蔽掉的权限位。默认权限基准不同:文件默认权限是 666(rw-rw-rw-),目录默认是 777(rwxrwxrwx)。创建时用默认基准减去 umask,得到实际权限。目录需要 x(进入)位,所以默认 777 减去 umask;文件默认 666 是因为文件通常不需要执行位,除非明确设置(如脚本要 chmod +x)。因此 umask 022 时:文件为 666-022=644,目录为 777-022=755。行为不同的本质:文件初始不含执行位(防止意外创建可执行文件),目录初始全含执行位。所以同一 umask 下,文件与目录的最终权限在 x 位上有差异。这是系统设计的安全考量——不让普通文本文件默认可执行。

核心是"基准不同:文件 666、目录 777"。文件不带 x 位避免意外可执行,目录需要 x 位才能进入,故同 umask 下二者最终权限不同。

umask 022
touch f; mkdir d
ls -l f d   # f: -rw-r--r-- (644), d: drwxr-xr-x (755)
#
★★★

18. 大文件日志按时间切割的 rotate 策略,logrotate 的 copytruncate 与 create 模式选哪种

大文件日志按时间切割的 rotate 策略如何设计?logrotate 的 copytruncate 与 create 模式如何选择?

  • 日志 rotate 的按时间策略
  • copytruncate 与 create 的机制差异
  • 选型与取舍

按时间切割指日志按时间周期(如 daily、hourly)轮转,保留固定份数,避免单个日志无限增长。copytruncate 模式:先复制日志内容到滚动文件,再截断原文件,进程无需重新打开文件即可继续写,适合"无法重启/不配合 reopen"的应用(如某些守护进程),但会丢失两次复制之间写入的部分数据,且复制大文件有性能开销。create 模式:rotate 后重命名原文件为滚动文件,再创建新的空日志文件,应用需重新打开日志(配合 copytruncate 或应用内 reopen/DLL 信号),无数据丢失但依赖应用支持 reopen。选型:若应用支持 SIGHUP/reopen(如 nginx、rsyslog),用 create 更可靠;若应用无法 reopen 又不想重启,只能 copytruncate,但要接受少量数据丢失,且不适合需精确行数的一次性定界。大文件场景建议 create + 应用 reopen,并设置 rotate 份数与压缩(compress)。

核心是"能否让应用重新打开日志"。create 无丢失但需 reopen,copytruncate 不需 reopen 但可能丢尾巴数据。大文件用 create+reopen 更稳。

# /etc/logrotate.d/app
/var/log/app/*.log {
    daily
    rotate 30
    compress
    notifempty
    create 0644 app app
    # 或 copytruncate
    postrotate
        systemctl reload app
    endscript
}
#
★★

19. ACL mask 在 ACL 中的最大有效权限语义与误用陷阱

ACL 中的 mask 表示"最大有效权限",其语义是什么?常见误用陷阱有哪些?

  • mask 对命名条目的约束
  • 与 group 位的关系
  • 误用陷阱

ACL 的 mask 是"命名用户/命名组/group 位"的最大有效权限上限,即这些条目的实际生效权限 = 该条目自身权限 ∩ mask。mask 不限制 owner 与 other。语义上,mask 相当于"还可授予多少个并集权限"的闸门。常见误用陷阱:一是 chmod 会重算 mask,导致命名用户的权限被意外收窄(如 chmod g-w 后命名用户失去写权限,实为 mask 改变);二是误以为 mask 是"额外权限",实际它是"上限",即使 mask 有 rwx,某条目不授予的权限仍无效;三是用 ls -l 看 group 位却不理解它现在代表 mask,而非真实组权限;四是 setfacl 添加命名条目时 mask 自动更新为"最大并集",可能掩盖想收紧的意图。正确做法:用 getfacl 查看 mask,必要时用 setfacl -m m::r-x 显式收紧,并理解"有效权限=条目∩mask"。

核心是"mask 是上限而非加权限"。命名条目的有效权限是自身与 mask 的交集,chmod 会重算 mask,这是最常见的坑。

setfacl -m u:alice:rwx /data
setfacl -m m::r-x /data     # 显式收紧 mask,alice 有效权限变为 r-x
getfacl /data
#
★★

20. GNU find 的 -print0 与 xargs -0 的工程意义,避免文件名包含特殊字符崩溃

GNU find 的 -print0 与 xargs -0 的工程意义是什么?为何能避免文件名含特殊字符导致的崩溃?

  • 文件名可能包含的空格/换行/引号
  • -print0 用 NUL 分隔
  • xargs -0 按 NUL 解析

文件名可以包含空格、制表符、换行、引号、$ 等特殊字符。默认 find 换行分隔输出,xargs 按空白分隔并做引号解释,一旦文件名含空格/换行,会被错误切分,导致命令执行错误甚至误删文件。-print0 让 find 用 NUL(\0)分隔每条路径,NUL 是文件名中不可能出现的字符(POSIX 文件名不允许 NUL);xargs -0 把输入按 NUL 切分,不做空白/引号解释,从而安全、无歧义地传递任意文件名。工程意义:在批处理海量或含特殊字符文件时,保证正确性与安全性,避免参数被拆分、命令注入或误操作。这是 shell 脚本处理"任意文件名"的健壮性基础。

核心是"NUL 是文件名中不存在的分隔符"。用 NUL 分隔 + 按 NUL 解析,彻底消除空白/引号/换行带来的歧义,是处理任意文件名的标准做法。

# 安全:NUL 分隔传递
find /data -name '*.log' -print0 | xargs -0 rm -f
# 不安全:换行/空格会导致错误
find /data -name '*.log' | xargs rm -f
#
★★

21. Linux 中 ext4 多块分配与延迟分配的影响

ext4 的多块分配(multiblock allocator)与延迟分配(delayed allocation)对性能和磁盘空间有何影响?

  • 多块分配减少碎片
  • 延迟分配聚合写入
  • 对掉电与空间的影响

多块分配(mballoc):ext4 一次性为一次写入分配多个连续块,减少元数据变更与碎片,提升顺序写性能。延迟分配(delayed allocation):数据先缓存在内存,稍后统一分配块并落盘,使写入尽量聚合为连续块,减少碎片、降低 I/O 次数,提升吞吐。影响:正面上提升大文件顺序写性能、减少文件碎片;负面上延迟分配使数据落盘滞后的窗口变长,掉电/崩溃时可能丢失更多未落盘数据,且空间分配延迟导致"磁盘空间看似未满但写入失败"(由预留与延迟分配差异造成),对备份与一致性要求高的场景需配合 fsync。总之 ext4 性能好但需理解其"延迟落盘"与"空间统计"特性,数据库建议用 data=ordered 配 fsync 保证持久性。

核心是"性能与一致性的权衡"。多块/延迟分配提升吞吐减少碎片,但延迟落盘带来掉电丢数据风险与空间统计差异,需 fsync 配合。

# 查看 ext4 挂载选项
mount | grep ext4
# 强制同步(fsync 语义)
dd if=/dev/zero of=/data/f bs=1M count=100 conv=fsync
#
★★

22. Linux 文件的 ctime、mtime、atime 分别代表什么及典型修改场景

Linux 文件的 ctime、mtime、atime 三时间戳分别代表什么?各自的典型修改场景是什么?

  • atime/mtime/ctime 定义
  • 各自触发修改的操作
  • 与 ls/stat 的查法

atime(access time):文件内容最后被访问(读)的时间,读文件会更新,但现代系统用 relatime 避免频繁更新。mtime(modification time):文件内容最后被修改的时间,写入内容时更新。ctime(change time):文件"状态"(inode 元数据)最后被改变的时间,任何修改内容、权限、属主、链接数、mv 重命名都会更新 ctime,且 ctime 无法被用户手动设置(自动更新)。典型场景:编写文件更新 mtime;chmod/chown/ln/重命名更新 ctime;读文件更新 atime(relatime 下首次读或间隔较久更新)。三者中 mtime 与 ctime 常被用于备份增量判断(如 tar 用 mtime,rsync 用 mtime+size)。注意 ctime 常被误当作"创建时间",实际是"状态变更时间"。

核心是区分"访问/修改/状态变更"。mtime 是内容改,ctime 是元数据/状态改且不可手动设,atime 是访问。备份与同步常以 mtime 为依据。

stat file.txt        # 显示 atime/mtime/ctime
ls -l file.txt       # 显示 mtime
ls -lc file.txt      # 显示 ctime
ls -lu file.txt      # 显示 atime
#
★★

23. Linux 文件系统 journaling 模式 data=ordered/writeback/journal 的崩溃一致性边界

Linux 文件系统 journaling 的 data=ordered、writeback、journal 三种模式在崩溃一致性上分别提供什么保证?

  • 三种 data 模式的语义
  • 元数据与数据的一致性保证
  • 崩溃场景的边界

journaling 先记录元数据(有时含数据)到日志,再提交,崩溃时按日志回放保证一致性。data 模式决定数据块如何处理。data=ordered(默认):先写数据再写元数据日志,保证"数据先于引用它的元数据落盘",崩溃后文件数据与元数据状态一致,不会出现"元数据指向未写数据"的脏状态,但单个文件可能包含部分旧数据(不保证事务原子性)。data=writeback:只对元数据做 journal,数据不保证顺序,性能最好但崩溃时可能出现"元数据已更新但数据未落盘"的脏块/数据损坏。data=journal:数据也写入日志,保证数据与元数据一致且 crash 后可完整回滚,最安全但写入压力大、性能差。选型:默认 ordered 兼顾性能与安全;对数据可靠性要求极高用 journal;追求极致性能可接受脏数据用 writeback。数据库等应用通常仍依赖自身 fsync 而非依赖 journal 模式。

核心是"数据是否进日志/是否保证顺序"。ordered 保证数据先于元数据,writeback 只有元数据日志,journal 数据也入日志。安全性与性能从低到高反向。

# 查看/设置挂载 data 模式
mount -o data=ordered /dev/sda1 /data
# 或 fstab
/dev/sda1 /data ext4 defaults,data=ordered 0 0
#
★★

24. Linux 软 RAID mdadm 与硬件 RAID 在文件系统层的差异

Linux 软 RAID(mdadm)与硬件 RAID 在文件系统层面有哪些差异?

  • 软 RAID 与硬件 RAID 的实现层
  • 对文件系统与 OS 的可见性
  • 性能与可靠性差异

软 RAID(mdadm)由内核 md 驱动实现,在块设备层把多个磁盘组成 RAID 设备,对文件系统透明(文件系统看到的是 /dev/md0 一个块设备),缺点是占用 CPU、依赖 OS,但无硬件成本、可移植性好、支持任意磁盘组合。硬件 RAID 由 RAID 卡独立处理,有自己的缓存(含电池保护)与处理器,不占主机 CPU,对 OS 呈现为单个逻辑磁盘,性能与可靠性通常更高,但依赖硬件、成本高、卡故障可能影响数据。文件系统层差异:两者对文件系统都呈现为"一个块设备",文件系统(ext4/xfs)本身不感知底层是软还是硬 RAID;差异在于缓存与写策略——硬件 RAID 的写缓存(带电池)可提升并保证 fsync 语义,软 RAID 无独立缓存但依赖内核;故障处理上,软 RAID 用 mdadm 查看/重建,硬件 RAID 用厂商工具(如 megacli)。选型考虑成本、性能、维护与硬件可靠性的权衡。

核心是"实现层不同但都呈现为块设备给文件系统"。软 RAID 靠内核、省成本、用 mdadm 管理;硬件 RAID 靠卡、性能好、用厂商工具管理,文件系统层差异主要在于缓存与 fsync 语义。

# 软 RAID 创建与查看
mdadm --create /dev/md0 --level=1 --raid-devices=2 /dev/sdb /dev/sdc
cat /proc/mdstat
mdadm --detail /dev/md0
#
★★

25. POSIX 文件能力与 setuid 二进制的能力赋权差异

POSIX 文件能力(file capabilities)与 setuid 二进制在能力赋权上有何差异?

  • setuid 的整体提权
  • file capabilities 的细粒度能力
  • 安全与运维选择

setuid 二进制在执行时以属主(通常 root)身份运行,获得全部特权,是一种"全有或全无"的提权,一旦被利用提权面大。file capabilities 是 Linux 引入的细粒度能力机制,通过 setcap 为特定可执行文件赋予"特定能力"(如 CAP_NET_BIND_SERVICE 绑定低端口、CAP_DAC_OVERRIDE 绕过文件权限),进程只在执行该文件时获得这些能力,而非全部 root,实现最小权限。差异:setuid 是"以 root 身份运行"(能力全集),file capabilities 是"只授所需能力"(能力子集),更安全、更可控。运维:用 setcap cap_net_bind_service=+ep /usr/bin/app 替代 4755,用 getcap 查看,setcap -r 移除。利用 file capabilities 可减少 setuid 二进制数量,降低提权面,是安全加固的推荐方向。

核心是"整体提权 vs 细粒度授权"。setuid 给全部 root 权限,file capabilities 只给特定能力,实现最小权限,降低风险。

# 用能力替代 setuid
setcap cap_net_bind_service=+ep /usr/bin/app
getcap /usr/bin/app
# 移除能力
setcap -r /usr/bin/app
#
★★

26. POSIX 文本文件末尾换行的工程意义与 git diff 的表现

POSIX 文本文件末尾换行的工程意义是什么?git diff 对有无末尾换行的表现有什么不同?

  • POSIX 行定义与末尾换行
  • git diff 的显示差异
  • 工程规范

POSIX 定义"行"以换行符结尾,标准文本文件最后一个字符应是为换行。省略末尾换行,某些工具(cat、wc、awk、shell 拼接)行为会有差异,且易导致多行拼接异常。工程意义:规范文件末尾换行保证每行完整、工具处理一致、diff 更干净。git diff 表现:文件末尾有换行时 diff 正常;无末尾换行时,git diff 会显示 \ No newline at end of file 标记,且每次修改该文件都可能产生额外 diff 行,造成"无意义变更"、合并冲突。因此工程上普遍要求文件末尾保留换行(编辑器/IDE 配置),CI 用 git 的 core.whitespace 或 lint 检查。git 的 autocrlf/core.autocrlf 也会影响换行,需统一团队规范。

核心是"末尾换行是 POSIX 行定义的组成部分"。它影响工具一致性,且 git diff 会以 \ No newline at end of file 标记,破坏 diff 简洁性。

# 检查文件末尾是否有换行
tail -c 1 file.txt | od -An -t x1   # 应为 0a
# 补充末尾换行
printf '\n' >> file.txt
#
★★

27. bash、dash、zsh 在脚本场景下的差异,sh 链接到 dash 的兼容性

bash、dash、zsh 在脚本场景下有哪些差异?sh 链接到 dash 的兼容性问题是什么?

  • bash/dash/zsh 的特性差异
  • sh 与 dash 的关系
  • 脚本兼容性

bash 是功能最丰富的 shell,支持数组、进程替换、[[ ]]、== 等扩展语法,是 RHEL 系默认脚本 shell;dash 是 Debian 系的 /bin/sh,追求轻量、POSIX 兼容、启动快,但缺少 bash 的数组、[[ ]]、进程替换等扩展;zsh 交互体验好、功能多,默认脚本兼容性一般。sh 链接到 dash 的兼容性:在 Debian/Ubuntu 上 /bin/sh 是 dash,若脚本用 bash 特有语法(如数组、[[ ]]、$(<file))却被声明为 #!/bin/sh 执行,会因 dash 不支持而报错。因此脚本应明确 shebang:需要 bash 特性用 #!/bin/bash,纯 POSIX 脚本用 #!/bin/sh 以保证 dash 兼容。工程上建议脚本声明正确的解释器,并避免依赖默认 shell 的差异。

核心是"shell 特性差异 + shebang 声明"。dash 是 POSIX 精简 shell,bash 扩展多,sh 指向 dash 时需避免 bash 特有语法。

#!/bin/bash          # 用 bash 特性时
echo $((a+1))
# POSIX 兼容脚本用 #!/bin/sh
#!/bin/sh
for i in 1 2 3; do echo $i; done
#
★★

28. chattr +i、+a 的工作机制与对 root 提权的影响边界

chattr +i、+a 的工作机制是什么?它们对 root 提权的影响边界如何?

  • +i 不可变、+a 只追加
  • 对 root 的限制
  • 安全加固边界

chattr +i 设置文件为不可变(immutable),即使 root 也不能修改、删除、重命名、创建硬链接;+a 设置只追加(append-only),只能追加内容不能改写/删除。工作机制:这些属性记录在文件系统 inode 的 flags 中,由内核在读写时强制检查,对 root 也生效(root 不能覆盖),但 chattr 本身需 root 权限。影响边界:+i/+a 可有效防止配置/日志被篡改,是加固手段,但并非绝对——它无法阻止内核级攻击、也不能防止有人先 chattr -i 解除(需要 root 与文件系统支持);且 +i 在启动/紧急修复时可能阻碍修改(需先解除)。对 root 提权的影响:+i/+a 能阻止提权后的恶意进程修改关键文件,但前提是 root 无法轻易解除;在某些场景(如备份、系统更新)需显式解除属性。注意:chattr 对某些文件系统(如 ext4 支持,xfs 部分支持,NFS 不一定)支持有限。

核心是"属性由内核强制,对 root 也生效"。+i 防改防删、+a 只追加,是加固手段,但边界是需 root 解除且受文件系统支持限制。

chattr +i /etc/passwd.bak   # 不可变
chattr +a /var/log/app.log  # 只追加
lsattr /etc/passwd.bak
chattr -i /etc/passwd.bak   # 解除
#
★★

29. chflags 在 BSD 与 chattr 在 Linux 的等价性差异

chflags 在 BSD 与 chattr 在 Linux 上的等价性与差异是什么?

  • chflags 与 chattr 的对应
  • 支持的属性与系统差异
  • 语法差异

chflags(BSD/macOS)与 chattr(Linux)都是设置文件系统扩展属性(flags)的工具,用于保护文件。chflags 有 uchg(user immutable,对应 chattr 的 +i)、uappnd(append-only,对应 +a)、schg(system immutable,仅 root 可设)等;chattr 有 i(immutable)、a(append-only)、s(secure delete)、u(undelete)、d(no dump)等。差异:一是命令与语法不同,chflags 用 uchg/uappnd 等命名,chattr 用单个字母属性;二是属性集合与语义有差异(如 schg 需要 root 且更严格,chattr 的 s/u 在 Linux 上未必支持);三是支持的文件系统不同,chattr 的某些属性在 ext4 支持、xfs/btrfs 部分支持、NFS 不支持;四是权限模型不同,chflags 的 schg 需要更严格的权限。运维跨系统需注意不可直接照搬命令,需按平台调整。

核心是"功能等价但命令与属性不同"。chflags 用 uchg/uappnd/schg,chattr 用 i/a/s/u,且跨系统支持度与权限语义有差异。

# BSD/macOS
chflags uchg /etc/passwd   # 不可变
chflags nouchg /etc/passwd # 解除
# Linux
chattr +i /etc/passwd
chattr -i /etc/passwd
#
★★

30. column、pr、tsv 在文本对齐与展示上的应用

column、pr、tsv 等工具在文本对齐与展示上的应用有何异同?

  • column 的列对齐
  • pr 的分页格式化
  • tsv 的表格处理

column 把文本按分隔符排列成对齐的列,column -t -s: /etc/passwd 按 : 分隔并对齐,适合把字段均匀展示;column -c 100 可限制输出宽度。pr 是分页/分栏格式化工具,把文件按页、按列排版输出(pr -2 双栏、pr -l 限行),用于打印或分页展示。tsv(tab-separated values)是 tab 分隔的表格数据格式,本身不是工具,常配合 column/awk/cut 处理;awk 的 printf 可按列精确定宽对齐。应用差异:column 适合"把 csv/分隔文本转成对齐表格",pr 适合"按页分栏排版",tsv 是数据格式,用于交换与程序处理。运维中看日志/配置文件对齐用 column -t,分页展示用 pr,表格数据处理用 awk/tsv。

核心是"对齐工具 vs 分页工具 vs 数据格式"。column 对齐列,pr 分页分栏,tsv 是数据格式,三者配合实现不同展示需求。

column -t -s: /etc/passwd
pr -2 -w 120 file.txt
awk -F'\t' '{printf "%s\t%s\n", $1, $2}' data.tsv
#
★★

31. cp -a、cp -r、cp -L 区别,保留权限与硬链接时为何用 cp -a

cp -a、cp -r、cp -L 的区别是什么?保留权限与硬链接时为何用 cp -a?

  • cp -r/-a/-L 的语义
  • -a 的完整保留
  • 硬链接与权限保留

cp -r 递归复制目录,但不保留权限、时间戳、符号链接等属性(默认按新默认值);cp -a(archive)等价于 -dR --preserve=all,递归复制并保留权限、属主、时间戳、符号链接、硬链接、ACL 等一切属性;cp -L 跟随符号链接复制目标文件(不保留链接本身,复制链接指向的内容)。为何用 cp -a:做备份或迁移时,需要完整保留文件权限、属主/属组、时间戳、符号链接与硬链接关系,cp -a 一次开关即可全部保留,保证复制后文件属性与行为一致;而 cp -r 会丢属性(如权限变默认、硬链接被拆成独立副本),cp -L 会丢掉符号链接本身。因此备份/镜像用 cp -a,普通复制用 cp -r,需要解引用符号链接用 cp -L。

核心是"完整保留 vs 简单复制"。-a 保留属性/链接/权限,-r 只递归不讲究属性,-L 跟随符号链接。备份迁移必须 -a。

cp -a /data /backup          # 完整保留属性和链接
cp -r /data /tmp             # 仅递归
cp -L /data/link /tmp/       # 解引用符号链接
#
★★

32. diff、patch 在配置漂移检测中的作用,patch 失败时如何 dry-run

diff、patch 在配置漂移检测中的作用是什么?patch 失败时如何 dry-run?

  • diff/patch 的用途
  • 配置漂移检测
  • patch dry-run 与回滚

diff 比较两个文件/目录的差异(diff -u 生成统一格式补丁),patch 应用补丁到文件。在配置漂移检测中:用 diff 对比"期望配置"与"实际配置"(如 diff 模板 vs 现网文件),发现偏差即漂移;用 patch 把差异应用到目标使其回到期望状态(或把修复应用到多个一致节点)。patch 失败时 dry-run:patch --dry-run -p1 < file.patchpatch -p1 --dry-run 只模拟应用、不实际修改,用于预检补丁能否干净应用、是否已应用、冲突位置;若失败,先检查补丁目标、路径层级(-p 数)、是否已应用,再决定是否强制或调整。diff 还可用于审计变更、检测被篡改。运维上结合配置管理(如 ansible)做漂移检测与修复。

核心是"diff 找差异、patch 应用补丁"。dry-run 用 --dry-run 预检,避免直接改坏文件;漂移检测靠 diff 期望 vs 实际。

diff -u /etc/nginx.conf.template /etc/nginx/nginx.conf
# 生成与应用补丁
diff -u old new > change.patch
patch --dry-run -p1 < change.patch   # 预检
patch -p1 < change.patch
#
★★

33. envsubst、gettext 在配置文件模板替换中的差异

envsubst、gettext 在配置文件模板替换中的差异是什么?

  • envsubst 的环境变量替换
  • gettext 的国际化
  • 选型

envsubst(来自 gettext 工具集)把输入中的环境变量引用(${VAR} 或 $VAR)替换为当前环境变量的值,用于把模板渲染成具体配置,envsubst < template.conf > out.conf,可用 envsubst '${VAR}' 限定。gettext 是国际化(i18n)框架,通过 gettext/msgfmt 管理 .po/.mo 翻译文件,在运行时把字符串按 locale 翻译,用于多语言界面文本,而非配置文件变量替换。差异:envsubst 是"运行时环境变量替换模板",gettext 是"按语言翻译字符串"。配置模板替换场景用 envsubst(或 sed/模板引擎);应用多语言用 gettext。选型:简单配置注入用 envsubst;需要多语言 UI 用 gettext。注意 envsubst 只替换已知环境变量,未定义变量会留空或保留,需注意。

核心是"模板变量替换 vs 国际化翻译"。envsubst 渲染运行时配置,gettext 做多语言,用途不同。

# envsubst 渲染配置模板
export DB_HOST=db1
envsubst < app.conf.tmpl > app.conf
# gettext 翻译
gettext -d hello "Hello World"
#
★★

34. fallocate、truncate、dd 在创建稀疏文件时的差异

fallocate、truncate、dd 在创建稀疏文件(sparse file)时的差异是什么?

  • 稀疏文件的原理
  • 三个工具的行为差异
  • 性能与占用

稀疏文件是逻辑上大但实际只占部分磁盘空间的"空洞"文件,空洞部分不实际分配块。fallocate 是预分配块(可能不写数据),用于快速预留空间,但默认会实际分配块(-n 表示保持文件长度不变,不产生空洞);truncate -s 可快速创建指定大小的稀疏文件(空洞不占空间),也用于调整大小;dd 写入数据并填充,dd if=/dev/zero bs=1M count=100 会真实写入块(占空间),用于生成真实数据文件,但 dd ... seek=... 可创建空洞。差异:靠"空洞"省空间的是 truncate(默认稀疏)与 fallocate -d(dig-holes 把已分配的空零区域转为空洞);fallocate 默认为预分配(占空间);dd 默认真实写入(占空间),除非 seek 跳过。性能:truncate/fallocate 创建稀疏文件极快,dd 真实写入慢。应用:创建大而稀疏的日志/镜像占位用 truncate,需真实数据用 dd,需预分配避免碎片用 fallocate。

核心是"是否真正分配块"。truncate 建空洞(稀疏),dd/fallocate 默认真实分配,fallocate -d 可挖洞。看是否 seek 跳过。

truncate -s 10G sparse.img      # 稀疏,几乎不占空间
fallocate -l 10G prealloc.img    # 预分配,占空间
dd if=/dev/zero of=data.img bs=1M count=100 seek=1000  # 空洞+部分数据
du -h sparse.img prealloc.img data.img
#
★★

35. filefrag、xfs_info、dumpe2fs 查看碎片与文件系统元数据的差异

filefrag、xfs_info、dumpe2fs 在查看碎片与文件系统元数据时的差异是什么?

  • 三个工具的对象
  • 碎片与元数据信息
  • 适用文件系统

filefrag 查看文件在磁盘上的碎片/块分布(extents 数量、连续性),用于评估文件碎片化程度,filefrag -v file 显示每个 extent。dumpe2fs 是 ext2/3/4 专用工具,dump 文件系统超级块、块组、inode 表等元数据,用于查看 FS 结构、特性、坏块统计。xfs_info 是 xfs 专用工具,显示 xfs 文件系统的块大小、inode 数量、ag 布局、log 大小等元数据。差异:filefrag 针对"单个文件的碎片",与文件系统类型无关(通用);dumpe2fs 针对 ext 系列的"文件系统整体元数据";xfs_info 针对 xfs 的"文件系统布局元数据"。选型:看文件碎片用 filefrag,看 ext 元数据用 dumpe2fs,看 xfs 元数据用 xfs_info。碎片化影响性能,ext4 用 e4defrag 整理,xfs 靠其分配策略。

核心是"文件碎片 vs 文件系统元数据 + 文件系统类型"。filefrag 通用看单文件碎片,dumpe2fs 看 ext 元数据,xfs_info 看 xfs 元数据。

filefrag -v /data/bigfile.img
dumpe2fs -h /dev/sda1
xfs_info /dev/sda1
#
★★

36. grep --color 与 ripgrep --color 在终端输出上的取舍

grep --color 与 ripgrep --color 在终端输出上的取舍是什么?

  • 两者颜色输出的差异
  • 性能与功能
  • 选型

grep --color 输出匹配文本高亮,支持 --color=auto/always/never,可用 GREP_COLORS 定制;ripgrep(rg)默认在终端输出颜色,支持 --color auto/always/never,并默认递归搜索、忽略 .gitignore、性能远超 grep。取舍:grep 是 GNU 标准、几乎无处不在、兼容性好,但单文件搜索性能低于 rg;rg 更快、默认遵循 .gitignore 忽略规则、输出更友好(带文件路径彩色、上下文),但依赖安装。颜色上:grep 需显式 --color 或别名,rg 默认彩色;管道输出时两者默认关闭颜色(--color=auto 检测终端),避免污染管道。生产 log 分析:单文件快速过滤用 grep(内置),大仓库/递归搜索用 rg(性能)。--color=always 用于管道输出时需配合 less -R 等支持颜色的查看器处理。

核心是"标准 vs 性能/体验"。grep 通用标准,rg 性能好默认彩色,二者都需注意管道时颜色处理。

grep --color=auto 'error' app.log
rg 'error' --color=auto app.log
# 管道时会关闭颜色
rg 'error' app.log | cat
#
★★

37. less、more、tail -f 与 journalctl -f 在跟踪日志时的差别

less、more、tail -f、journalctl -f 在跟踪日志时的差别是什么?

  • 不同工具的分页/跟踪能力
  • 实时跟踪与检索
  • 选型

more 是基础分页器,只能向前翻页,功能简单;less 是增强分页器,支持前后翻页、搜索(/)、键盘导航、跟随文件(less +F 或 Shift-F),适合查看大文件;tail -f 实时监控文件追加内容,tail -F 还能在日志 rotate 后继续跟踪(重开文件),是日志实时跟踪的常用方式;journalctl -f 是 systemd journal 的实时跟踪,journalctl -u 服务过滤、-f 跟随、-k 内核日志、--since 时间段,用于 systemd 管理的日志。差别:查看历史分页用 less,实时跟踪文件追加用 tail -f/-F,跟踪 systemd 日志用 journalctl -f。tail -f 只管文件追加,journalctl -f 能按服务/时间过滤并结构化,less 适合静态浏览大文件。生产排障常组合:tail -f 看应用日志,journalctl -f 看 systemd 服务日志。

核心是"看静态 vs 跟实时 vs 系统日志"。less 分页浏览,tail -f 跟文件追加,journalctl -f 跟 systemd 日志且可过滤。

tail -F /var/log/app.log       # 实时跟踪,rotate 后继续
journalctl -f -u app.service   # 跟踪服务日志
less +F /var/log/app.log        # less 跟随模式
#
★★

38. mktemp 在创建临时文件时的安全实践,避免 /tmp 竞争

mktemp 创建临时文件时的安全实践是什么?如何避免 /tmp 竞争(TOCTOU)?

  • mktemp 的随机安全创建
  • /tmp 的共享可写与竞争
  • 安全实践

/tmp 是公共可写目录,直接用固定文件名(如 /tmp/tmp.log)存在竞态与符号链接攻击风险(TOCTOU:检查与使用之间被替换,攻击者可预创建同名文件或符号链接指向敏感文件)。mktemp 用随机唯一名创建文件/目录,避免名字猜测与竞态:mktemp 创建随机临时文件,mktemp -d 创建临时目录,mktemp /tmp/xxx.XXXXXX 自定义模板。安全实践:一是用 mktemp 而非硬编码文件名,获得唯一路径;二是创建后立即用该路径,避免权限问题(mktemp 默认 0600);三是临时目录可设 trap 清理(trap 'rm -rf "$TMPDIR"' EXIT);四是避免在 /tmp 用可预测名并做"检查存在性"再使用(TOCTOU)。这能防止符号链接攻击与数据泄露。

核心是"随机唯一名 + 避免可预测名 + 及时清理"。mktemp 解决 TOCTOU 名字竞争,配合 trap 清理防泄漏。

TMPDIR=$(mktemp -d)
trap 'rm -rf "$TMPDIR"' EXIT
echo data > "$TMPDIR/file"
#
★★

39. mount --bind 将目录绑定到 NFS 挂载点时的行为与风险,以及 bind 后挂载选项与安全位如何继承、NFS 与本地文件系统权限语义差异如何排查

mount --bind 将目录绑定到 NFS 挂载点时的行为与风险是什么?bind 后挂载选项与安全位如何继承?NFS 与本地文件系统的权限语义差异如何排查?

  • mount --bind 的语义
  • bind 后选项/安全位继承
  • NFS 与本地权限差异排查

mount --bind 把已挂载的目录"重新挂载"到另一路径,使同一目录在两处可见。行为:bind 不改变底层文件系统,只是把挂载点重定向到现有目录的 inode。风险:bind 后默认继承原始挂载的选项(如 nosuid、nodev、noexec 按原挂载点),若原挂载点未收紧,bind 会继承宽松选项;因此建议 bind 后用 mount -o remount,noexec,nosuid,nodev 显式收紧需要设置的安全位。NFS 与本地权限差异:NFS 的权限(root_squash、uid/gid 映射、no_root_squash)与本地 uid/gid 语义不同,chmod 在 NFS 上可能受 NFS 服务器导出选项限制(如 root squash 使 root 映射为 nobody),表现与本地不一致。排查:用 mount | grep 看实际挂载选项,findmnt 看层级,stat 看权限,showmount/nfsstat 看 NFS 服务器导出选项,确认为何 chmod 行为不同。

核心是"bind 继承原挂载选项 + 需显式重设安全位 + NFS 权限语义差异"。bind 后要 remount 显式收紧,NFS 权限受导出选项与 squash 影响。

mount --bind /data /mnt/view
mount -o remount,noexec,nosuid,nodev /mnt/view   # 显式收紧安全位
findmnt -o TARGET,OPTIONS /mnt/view
mount | grep nfs
#
★★

40. mount --make-shared/--make-private/--make-slave 在容器命名空间的传递

mount --make-shared/--make-private/--make-slave 在容器命名空间中的传播行为是什么?

  • 挂载传播类型
  • 命名空间与 propagation
  • 容器场景

Linux 挂载传播(propagation)决定挂载事件如何在命名空间间传播。--make-shared:挂载/卸载事件在 peer 组间双向传播(A 挂载,B 可见);--make-private:挂载事件不传播到其他命名空间(双向隔离);--make-slave:单向传播——从 master 传播到 slave,但 slave 的挂载不反向传播;--make-unbindable 阻止再被 bind。容器场景:Docker 默认把主机的某些挂载以 slave/rprivate 传播。rootfs 通常用 private,/data 等卷用 shared 或 slave 以便主机挂载在容器可见。--make-shared 用于主机与容器共享挂载(如卷挂载),--make-private 用于隔离,--make-slave 用于"只读接收主机挂载但容器内挂载不反向泄露"。容器编排中,--make-shared 常用于让 NFS 等主机挂载在所有容器可见,slave 用于安全隔离。

核心是"挂载事件传播方向"。shared 双向、private 双向隔离、slave 单向(master→slave)。容器归根到底用 propagation 控制卷可见性。

mount --make-shared /data
mount --make-private /data
mount --make-slave /data
# 查看 propagation
findmnt -o TARGET,PROPAGATION /data
#
★★

41. rename.ul 在批量重命名时的可用性与替代方案

rename.ul 在批量重命名时的可用性如何?有哪些替代方案?

  • rename.ul 与 perl rename 的区别
  • 批量重命名语法
  • 替代方案

Linux 上有两个 rename:util-linux 的 rename.ul(简单替换,rename 'old' 'new' files)和 perl 的 rename(rename 's/old/new/' files,支持正则)。rename.ul 是 util-linux 提供的基本替换版本,语法简单:rename.ul 'a' 'b' file*.txt 把文件名中的 a 替换为 b。可用性:rename.ul 功能有限(仅字符串替换,不支持正则),且不同发行版 rename 指向不同(Debian 指 perl rename,RHEL 用 util-linux rename)。替代方案:perl rename 支持正则(rename 's/\.txt$/.log/' *.txt);批量重命名也可用 shell 循环 + mv(for f in *.txt; do mv "$f" "${f%.txt}.log"; done,支持参数扩展);或 mmv(更强大的批量重命名工具)。选型:简单替换用 rename.ul,正则需 perl rename 或 mmv,复杂场景用 shell 循环 + mv 最可控。

核心是"util-linux 简单替换 vs perl 正则 + 跨发行版差异"。批量重命名要确认 rename 版本,复杂规则用 shell 循环或 mmv。

rename.ul 'a' 'b' *.txt        # util-linux 简单替换
rename 's/\.txt$/.log/' *.txt  # perl rename 正则
for f in *.txt; do mv "$f" "${f%.txt}.log"; done
#
★★

42. sed -i 在 GNU/BSD 的差异,备份后缀 -i.bak 的兼容性

sed -i 在 GNU 与 BSD 上的差异是什么?备份后缀 -i.bak 的兼容性如何?

  • GNU/BSD sed -i 语法差异
  • -i.bak 兼容性
  • 跨平台脚本

GNU sed(Linux)的 sed -i 可直接修改,sed -i.bak 备份原文件为 .bak;BSD sed(macOS)要求 -i 后必须跟备份后缀(sed -i.baksed -i ''),单独 sed -i 报错。差异:GNU 允许 -i 无后缀,BSD 必须显式后缀(-i '' 表示无备份)。兼容性:用 -i.bak 在 GNU 和 BSD 都可用(GNU 备份为 .bak,BSD 同样),但语义上 GNU 的 -i.bak 与 BSD 相同;而 -i 无后缀在 BSD 报错。跨平台脚本建议:用 sed -i.bak 保证兼容,或检测平台(uname)选择语法;或用 -i ''(BSD 无备份)/ -i(GNU)。注意 BSD sed 的其它正则差异(如 \d 不支持)。工程上跨平台务必备份后缀并测试。

核心是"GNU 允许 -i 无后缀,BSD 必须带后缀"。用 -i.bak 兼容两者,跨平台脚本需检测或统一带后缀。

# GNU 可用
sed -i 's/a/b/g' file
# BSD 需后缀
sed -i.bak 's/a/b/g' file
# 兼容写法
sed -i.bak 's/a/b/g' file
#
★★

43. set -euo pipefail 在脚本中的工程价值,为何单独 set -e 不够

set -euo pipefail 在脚本中的工程价值是什么?为什么单独 set -e 不够?

  • set -e/-u/-o pipefail 各自语义
  • 单独 set -e 的不足
  • 健壮脚本实践

set -e(errexit):命令出错即退出脚本;set -u(nounset):使用未定义变量报错退出;set -o pipefail:管道中任一命令失败则整体失败(默认只取最后一个命令退出码)。单独 set -e 不够:一是 set -e 在管道中只检查最后一个命令,cmd1 | cmd2 中 cmd1 失败也会被忽略(无 pipefail);二是 set -e 在 if 条件、部分上下文(&&/||)中不触发,可能掩盖错误;三是未定义变量(set -u 缺失)会静默空值展开,导致误操作。组合 set -euo pipefail 让脚本"出错即停、变量必须定义、管道全链路检查",提高可靠性。这是 shell 脚本健壮性的工程基线。

核心是"三开关互补"。pipefail 补管道尾部检查、nounset 补未定义变量、errexit 补命令失败,三者组合才覆盖主要错误源。

set -euo pipefail
# 管道中任意命令失败都会退出
cmd1 | cmd2
# 未定义变量立即报错
: "$UNDEFINED"
#
★★

44. shell 变量 IFS 与引号误用导致的 word splitting 问题如何避免

shell 变量 IFS 与引号误用导致的 word splitting 问题如何避免?

  • word splitting 的触发
  • IFS 的作用
  • 引号的重要性

word splitting 是 shell 在扩展未加引号的变量时,按 IFS(默认空格/制表/换行)把结果拆成多个词,导致含空格的文件名/路径被拆开、参数被错误传递。例如 rm $file 在 file 含空格时变成多个参数,行为错误。避免方法:一是变量引用加双引号 "$var",这阻止 word splitting 与 globbing,是最重要实践;二是自定义 IFS 时注意(如 IFS=',' read -ra)只在需要处用;三是列表循环用 for x in "$@" 或数组,而非裸变量。IFS 只在未加引号的变量展开时生效,加引号后 IFS 不起作用。正确实践:所有变量引用一律 "$var",需要拆分时显式用数组或 read -a。这能从根本避免 word splitting 与 globbing 带来的脚本错误与安全风险。

核心是"引号阻止 word splitting + globbing"。未加引号的变量被 IFS 拆分,加双引号即安全,这是 shell 脚本头号 bug 来源。

file="a b.txt"
rm "$file"          # 正确:一个参数
rm $file            # 错误:拆成 rm a b.txt
IFS=',' read -ra arr <<< "$line"   # 显式按逗号拆分
#
★★

45. sort、uniq、awk 三者组合做日志去重的常见坑,时间字段排序为何要 -k1.5

sort、uniq、awk 组合做日志去重的常见坑有哪些?时间字段排序为何要用 -k1.5?

  • sort/uniq/awk 组合去重
  • 排序键与字段
  • -k1.5 的语义

去重常见做法:awk '{count[$1]++}' 统计,或 sort | uniq -c 排序后去重计数。常见坑:一是 uniq 只对相邻行去重,必须先 sort;二是 sort 默认按整行字典序,若想按某字段需 -k;三是时间字段排序:日志时间常为 2026-08-04 10:00:00 格式,若按字段切分,第一字段是日期、第二字段是时间,用 sort -k1.5 表示从第 1 字段的第 5 字符开始排序(跳过定宽日期的前 4 位年份如"2026",从第 5 字符起比较,从而正确区分月份),或直接用 -k1,1 -k2,2 分别按日期、时间排序。实际上 -k1.5 是"按第 1 字段从第 5 个字符开始"排序,用于正确处理"YYYY-MM-DD"这种定宽日期,避免按字典序出错的场景。awk 做去重统计用关联数组,不会因排序忽略非相邻重复,比 sort|uniq 更直接。

核心是"uniq 需相邻 + 排序键与定宽字段"。日志时间排序用 -k1.5 指定从第 1 字段第 5 字符起,正确处理定宽日期;awk 关联数组去重免排序。

# 去重计数
sort file | uniq -c
# 按时间字段排序(第1字段第5字符起)
sort -k1.5 file
# awk 去重统计
awk '{cnt[$0]++} END{for(k in cnt) print cnt[k], k}' file
#
★★

46. sudoedit 与 visudo 在特权文件编辑的安全边界

sudoedit 与 visudo 在特权文件编辑上的安全边界是什么?

  • sudoedit 的临时文件拷贝机制
  • visudo 的语法校验
  • 安全边界

sudoedit 让用户以"调用者身份"编辑特权文件:sudo 把文件复制到临时文件,用户用编辑器修改(编辑器以调用者身份运行,不继承 root 权限),保存后 sudo 校验并写回原文件。安全边界:一是用户编辑的是临时副本,无法直接改变文件权限/属主/链接,避免提权;二是编辑器陷阱等攻击面被限制在临时文件;三是要在 sudoers 中配置 Defaults editor 与允许的编辑文件。visudo 用于安全编辑 /etc/sudoers:保存前做语法校验,语法错误则拒绝保存,防止 sudoers 写坏导致 sudo 不可用(安全边界:防止锁定系统)。差别:sudoedit 是"以普通用户身份编辑特权文件"的安全机制,visudo 是"sudoers 专有编辑器的语法校验"。两者都强调"校验/隔离"防止破坏特权配置。

核心是"隔离编辑 + 语法校验"。sudoedit 用临时副本隔离防止提权,visudo 校验 sudoers 语法防锁死系统。

# sudoers 允许编辑
# user ALL=(root) NOPASSWD: /usr/bin/sudoedit /etc/nginx/nginx.conf
sudoedit /etc/nginx/nginx.conf
# visudo 校验语法
visudo -cf /etc/sudoers
#
★★

47. tail -F 与 tail -f 在日志被 rotate 后行为差异,为什么推荐 -F

tail -F 与 tail -f 在日志被 rotate(轮转)后的行为差异是什么?为什么推荐 -F?

  • tail -f 与 -F 的差异
  • rotate 后文件句柄
  • 推荐 -F

tail -f 跟踪文件时按文件描述符(inode)持续读取,当日志被 rotate(如 logrotate 重命名原文件并创建新文件)后,tail -f 仍指向旧 inode 的已重命名文件,看不到新日志内容;tail -F 会检测文件被替换/重命名,自动重新打开新文件继续跟踪。因此日志轮转频繁时,tail -F 能无缝跟随新日志,tail -f 会停在新旧文件之间。推荐 -F:生产日志通常被 logrotate 轮转,用 -F 才能持续看到最新内容,避免"日志停更"的假象。tail -F 等价于 tail --follow=name --retry,会跟随文件名并重试。

核心是"tail -f 跟随 inode、tail -F 跟随文件名"。rotate 后 -f 停旧文件,-F 自动重开新文件,日志轮转环境推荐 -F。

tail -f /var/log/app.log    # 按 inode,rotate 后停
tail -F /var/log/app.log    # 按文件名,rotate 后自动重开
#
★★

48. tar、cpio 与 rsync 在备份场景的差异及稀疏文件、硬链接、增量与带宽控制如何取舍

tar、cpio、rsync 在备份场景下的差异是什么?稀疏文件、硬链接、增量与带宽控制如何取舍?

  • tar/cpio/rsync 的备份特点
  • 稀疏文件、硬链接处理
  • 增量与带宽控制

tar 归档便于打包/传输,支持稀疏文件(--sparse)、硬链接(-h)、权限保留(-p),适合全量归档到文件;cpio 是较底层的归档工具,生成文件列表,适合与 find 配合(find | cpio -o),保留 inode 与硬链接,但使用较少;rsync 是同步工具,支持增量(只传变化块)、校验、断点续传、带宽控制(--bwlimit)、远程同步,适合备份/镜像。差异取舍:稀疏文件:tar 用 --sparse 处理,rsync 需显式加 -S/--sparse 才能高效处理稀疏文件;硬链接:tar 用 -h 保留,cpio 天然保留,rsync 用 -H 保留;增量:rsync 增量同步最灵活,tar 增量用 --listed-incremental 较复杂;带宽控制:rsync 用 --bwlimit,tar 无法控制带宽(需配合限速)。选型:打包镜像用 tar + sparse,点对点同步/增量备份用 rsync,底层归档用 cpio。整体上 rsync 在增量、校验、带宽上更强,tar 在归档打包上更通用。

核心是"归档 vs 同步"。tar 归档打包、rsync 增量/带宽/校验、cpio 底层归档,稀疏/硬链接三者都需显式选项处理。

tar --sparse -czf backup.tar.gz /data
rsync -aH --bwlimit=5000 /data/ backup/
find /data | cpio -o > backup.cpio
#
★★

49. truncate、dd、shred 在清空或销毁磁盘数据时各自的安全级别与差异

truncate、dd、shred 在清空或销毁磁盘数据时的安全级别与差异是什么?

  • 三个工具的清空/销毁机制
  • 安全级别
  • 适用场景

truncate 只调整文件大小(可清空文件内容至 0 长度),但底层数据块可能残留,不安全,仅用于逻辑清空。dd 用零/随机数据覆盖(dd if=/dev/zero of=dev bs=... 或 /dev/urandom),可覆盖整个磁盘/分区,但单次覆盖对 SSD 可能因磨损均衡/缓存的映射而无法完全擦除,且性能慢。shred 专门用于安全销毁:多次覆盖(默认 3 次,-n 可指定)、支持删除文件,但同样对 SSD、日志文件系统、有缓存的设备效果有限(可能无法覆盖所有物理块)。安全级别:truncate 无安全擦除(仅逻辑),dd 单次覆盖(对机械盘部分有效),shred 多次覆盖(更安全但非绝对)。对 SSD 建议用 blkdiscard/nvme format 或厂商安全擦除(secure erase),而非覆盖。销毁敏感数据需结合"覆盖 + 设备级安全擦除 + 介质销毁"。

核心是"安全擦除级别不同"。truncate 仅逻辑空、dd 覆盖、shred 多次覆盖,但 SSD/磨损均衡使覆盖不可靠,需设备级擦除。

truncate -s 0 file            # 逻辑清空,不销毁底层
dd if=/dev/zero of=/dev/sdb bs=1M   # 覆盖
shred -n 3 -z /dev/sdb        # 多次覆盖+清零
blkdiscard /dev/sdb           # SSD 丢弃块
#
★★

50. umask 027 与 umask 077 在多用户系统的安全等级差异

umask 027 与 umask 077 在多用户系统上的安全等级差异是什么?

  • 027 与 077 的默认权限
  • 属组与其他用户
  • 多用户安全

umask 027 表示新建文件默认 666-027=640(属主读写,属组读,其他无),目录默认 777-027=750(属主/属组可进,其他无权限)。umask 077 表示文件默认 600(仅属主读写),目录默认 700(仅属主可进)。差异:027 允许"同组用户"读(文件)和进入(目录),077 完全禁止其他用户包括同组,仅属主可访问。安全等级:077 更严格(仅属主),027 是"组内共享"的折中(适合多用户协作但组内可读)。多用户系统考量:若同一属组内有多个用户且需共享,用 027 便于组内读;若强调数据隔离(每个用户独立),用 077 防止同组窥探。选级:安全优先用 077,需组内协作用 027。注意:umask 含 2(如 002)即禁止"其他用户写",027 与 077 都禁其他用户。

核心是"other 与 group 的可见性"。027 允许组内读,077 仅属主,077 更安全,027 用于组内共享。

umask 027   # 文件 640,目录 750
umask 077   # 文件 600,目录 700
touch f; mkdir d; ls -l f d
#
★★

51. umask 在 systemd unit 文件与登录脚本中的取值差异

umask 在 systemd unit 文件与登录脚本中的取值差异是什么?

  • systemd unit 的 umask 配置
  • 登录脚本的 umask
  • 差异与影响

登录脚本(~/.bashrc、/etc/profile、/etc/login.defs)中 umask 在用户登录时生效,通常由 /etc/login.defs 的 UMASK 或 shell 配置文件设置,影响交互式 shell 中新建文件的默认权限。systemd unit 文件通过 UMask= 指令设置服务的 umask(默认继承 systemd 的默认 0022,除非显式设置),服务进程启动时即应用该 umask,与用户登录 shell 无关。差异:登录脚本的 umask 影响交互式会话,systemd 的 UMask 影响系统服务进程;systemd 服务可能不加载用户 shell 配置文件,因此需在 unit 中显式 UMask=0027 等。若服务创建文件权限不符合预期,应先检查 unit 的 UMask 而非登录脚本。另外 /etc/login.defs 的 UMASK 影响所有新用户登录的默认 umask。

核心是"作用对象不同"。登录脚本 umask 影响交互会话,systemd UMask 影响服务进程,服务需在 unit 显式设置。

# systemd unit
[Service]
UMask=0027
# 登录脚本
echo "umask 027" >> ~/.bashrc
#
★★

52. watch、stdbuf、unbuffer 让管道实时刷新如何配合使用

watch、stdbuf、unbuffer 让管道实时刷新如何配合使用?

  • watch 周期执行
  • stdbuf 关闭缓冲
  • unbuffer 伪终端

watch 周期性地执行命令并刷新输出(watch -n 1 'df -h'),适合周期性监控。但一些命令(如 top、tail)在管道中因输出缓冲而"不实时"刷新:stdbuf 可调整命令的 stdio 缓冲(stdbuf -oL cmd 行缓冲、-o0 无缓冲),让输出立即进入管道;unbuffer(expect 工具)为命令分配伪终端(PTY),使命令以为在交互终端运行,从而启用行缓冲/实时输出。配合:用 watch -n 1 'unbuffer 命令'stdbuf -oL cmd | watch 实现实时刷新。典型场景:watch -n 1 'stdbuf -oL tail -f /var/log/app.log' 持续刷新日志;监控命令输出实时变化。三者目标一致:让本会缓冲的输出实时显示在 watch 中。

核心是"缓冲问题"。stdbuf 关闭缓冲、unbuffer 用 PTY 模拟交互,让命令实时输出,配 watch 周期性刷新。

watch -n 1 'df -h'
stdbuf -oL tail -f /var/log/app.log | watch -n 1
watch -n 1 'unbuffer free -h'
#
★★

53. xargs -I{} 与 -n1 的性能差异,为什么大数据集应避免 -I

xargs -I{} 与 -n1 的性能差异是什么?为什么大数据集应避免 -I?

  • -I{} 每次替换执行
  • -n1 按批执行
  • 大数据集性能

xargs -I{} 把占位符 {} 替换为输入,每次输入项执行一次命令(等价于逐条执行,会 fork 大量子进程),性能差;xargs -n1 每累积 1 个参数执行一次命令(也逐条,但可配合 -n 更大的批量如 -n100 减少执行次数)。-I{} 会强制每次执行一条子命令(无法批量合并),且丢失多参数合并能力,子进程开销大;大数据集下会启动成千上万次进程,性能极低。相反,xargs -n100 cmd 把 100 个参数合并为一条命令执行,大幅减少进程数。因此大数据集应避免 -I{},改用 -n 批量或 -I 后用 xargs -n 折中。若必须逐条(如每条需独立上下文),可用 -n1 但意识其开销可接受。

核心是"逐条 vs 批量子进程开销"。-I{} 强制逐条执行、进程数多,大数据集应用 -n 批量合并参数减少 fork。

# 慢:-I{} 逐条执行
find . -type f | xargs -I{} cp {} /dest/
# 快:批量合并
find . -type f | xargs -n 100 cp -t /dest/
#
★★

54. xargs 与 parallel -j 在并行处理文件的性能差异

xargs 与 parallel -j 在并行处理文件时的性能差异是什么?

  • xargs -P 并行
  • parallel 的并行调度
  • 性能与功能

xargs 加 -P 可并行执行(xargs -P8 -n1 cmd),按简单方式分发任务,但调度较粗糙(无任务依赖、无输出同步、无资源感知),适合简单拆分并行。GNU parallel 是专门的并行工具,支持 -j 指定并行数、任务调度、输出按块同步、资源限制(--load/--memfree)、失败处理(--retries)、进度、按 CPU/机器分发,功能强大。性能差异:parallel 调度更精细(能保持输出顺序、处理任务失败重试、按负载动态调整),大数据集/复杂任务下更高效可控;xargs -P 简单直接、开销小、适合无状态批量。选型:简单并行拆分用 xargs -P,需要输出同步、失败重试、资源控制、复杂任务用 parallel。并行度受 CPU/IO 限制,需按负载调整 -j/-P。

核心是"简单并行 vs 精细调度"。xargs -P 简单直接,parallel 调度强(输出同步、重试、资源控制),大数据集用 parallel 更稳。

cat list | xargs -P8 -n1 cmd
cat list | parallel -j8 cmd
# parallel 输出同步与重试
cat list | parallel -j8 --retries 3 'cmd {}'
#
★★

55. 为什么 NFS 共享目录上的 chmod 行为可能与本地 ext4 不同

为什么 NFS 共享目录上的 chmod 行为可能与本地 ext4 不同?

  • NFS 权限模型
  • root_squash/squash 与 uid 映射
  • 与本地 FS 差异

NFS 的权限由"服务器端"文件系统最终决定,但 NFS 服务器导出选项改变了客户端视角的权限语义。差异原因:一是 root_squash:NFS 默认把客户端 root(uid 0)映射为 nobody(匿名),因此客户端 root 执行 chmod 可能权限不足或行为不同(本地 ext4 root 可任意 chmod);二是 uid/gid 映射:NFS 依赖客户端与服务器的 uid/gid 一致(或 idmap 映射),若客户端 uid 与服务器不一致,chmod 后属主显示/生效异常;三是导出选项(no_root_squash、options)影响权限控制;四是 NFS 的答错/缓存,chmod 结果可能因 ACL 或 POSIX 权限解释差异。排查:确认服务器端 squash 配置(exportfs -v)、客户端/服务器 uid 一致性、NFS 挂载选项(mount options)、用 stat 对比属性。本地 ext4 权限由本机内核直接检查,NFS 则叠加服务器 squash 与映射。

核心是"squash 与 uid 映射改变客户端权限语义"。root_squash 使 root 映射为 nobody、uid 不一致导致属主异常,是 NFS chmod 与本地不同的主因。

exportfs -v              # 服务器导出选项
mount | grep nfs         # 客户端挂载选项
getent passwd <uid>      # 检查 uid 映射
stat /mnt/nfs/file
#
★★

56. 为什么不应使用 chattr 给 /etc/passwd 加 +i,否则会影响用户创建

为什么不应使用 chattr 给 /etc/passwd 加 +i?否则会产生什么影响?

  • chattr +i 的不可变
  • /etc/passwd 的写操作
  • 影响范围

chattr +i 使 /etc/passwd 不可变,任何进程(包括 root 和系统)都无法修改、删除、重命名该文件。而 /etc/passwd 是系统用户数据库,useradd、usermod、passwd 等都会写入它(新增用户、修改用户信息);加 +i 后这些操作会失败(No such file or directory / Permission denied),导致无法创建新用户、修改用户、无法正常登录维护。影响:一是用户管理全部失效;二是虽然口令在 /etc/shadow,但创建用户仍需写 /etc/passwd;三是可能影响系统服务(如 getent/dbus 依赖)。为什么不建议:加固 /etc/passwd 用 +i 是常见误区,正确做法是限制对该文件的写权限并配合文件完整性监控(如 AIDE、auditd),而不是用 +i 阻断系统必要写入。若已加 +i,需 chattr -i 解除。

核心是"不可变文件阻断系统写入"。/etc/passwd 需被 useradd 等频繁写入,+i 会锁死用户管理功能,应改用权限控制与完整性监控。

# 错误:锁死用户管理
chattr +i /etc/passwd
useradd bob   # 失败
# 解除
chattr -i /etc/passwd
#
★★

57. 为什么脚本中 cat $file | grep 是不良写法,如何优化

为什么脚本中 cat $file | grep 是不良写法?如何优化?

  • UUOC(useless cat)
  • 未加引号变量
  • 优化方式

cat $file | grep 是不良写法:一是 UUOC(useless use of cat)——grep 本身支持读文件,cat | grep 多启动一个进程、多一层管道,无必要;二是 $file 未加引号,文件名含空格/通配符时会被拆分/展开,导致错误或安全问题;三是多进程管道降低性能。优化:grep pattern "$file"(grep 直接读文件,变量加引号);若需多文件,grep pattern file1 file2;递归用 grep -r。若要处理的是命令输出而非文件,则可直接管道(合理地用 cat)。工程规范:避免不必要的 cat,grep 直接读文件,变量始终加引号。

核心是"UUOC + 引号缺失"。grep 能直接读文件,cat | grep 多余且变量未加引号有拆分风险,优化为 grep 直接读并加引号。

# 不佳
cat $file | grep 'error'
# 优化
grep 'error' "$file"
#
★★

58. 在 CI 中调用 bash 的 -x、-v、set -o nounset 如何选择

在 CI 中调用 bash 的 -x、-v、set -o nounset 如何选择?

  • bash -x/-v 的调试
  • set -o nounset
  • CI 场景选择

bash -x 打印每条命令的执行(含展开后的变量值),用于调试命令实际执行;bash -v 打印每行原始输入(不回显展开),用于查看脚本源码;set -o nounset(-u)遇到未定义变量即报错退出,防止变量为空导致误操作。CI 场景选择:默认脚本开头 set -euo pipefail 保证健壮(出错即停、未定义变量报错、管道全检查);调试某步用 -x 查看实际执行(可用 set -x/set +x 局部开启);-v 用于排查语法/逻辑(查看原始行)。CI 中通常不开 -x 常开(会刷屏泄露变量),而是按需在失败步骤用 -x 排查;nounset 建议常开防变量错误。日志中 -x 会暴露敏感变量,需注意脱敏。

核心是"健壮性默认 + 按需调试"。CI 默认 set -euo pipefail,-x 按需调试(注意敏感信息),-v 看原始行。

#!/bin/bash
set -euo pipefail
# 局部调试
set -x
cmd
set +x
#
★★

59. 文件系统选型 xfs、ext4、btrfs 在大文件、快照、压缩场景下如何决策

xfs、ext4、btrfs 在大文件、快照、压缩场景下如何选型决策?

  • 三个文件系统的特性
  • 大文件/快照/压缩能力
  • 选型原则

ext4 成熟稳定、兼容性好,适合通用中小文件,支持大文件(若干 TB)但单文件上限与多大能力有限,快照/压缩需 LVM 辅助;xfs 面向大文件/大容量,单文件上限极大(8EB),分配效率高,适合海量数据、大文件、数据库这类场景,但缩减(shrink)与快照支持弱(需 LVM);btrfs 是写时复制(COW)文件系统,原生支持快照(subvolume)、压缩(compress=zstd/lzo)、校验和、RAID,但成熟度与稳定性在部分场景存疑,适合需要快照/压缩/去重的场景。决策:大文件/大容量/数据库用 xfs;通用稳定/小文件/兼容性优先用 ext4;需要原生快照/压缩/分层存储用 btrfs(需评估稳定性)。生产数据库常选 xfs 或 ext4,容器/快照需求多选 btrfs 或 overlay 配合。

核心是"按场景匹配特性"。xfs 大文件大容量、ext4 通用稳定、btrfs 快照压缩。

# 查看文件系统类型
df -T /data
# btrfs 快照与压缩
btrfs subvolume snapshot /data /data/.snap
mount -o compress=zstd /dev/sda1 /data
#

60. Linux 中 file 命令如何识别 ELF、shebang、文本编码

Linux 中 file 命令如何识别 ELF、shebang、文本编码?

  • file 的 magic 识别
  • ELF/shebang/编码
  • file 命令选项

file 通过 magic 数据库(/usr/share/misc/magic)识别文件类型。ELF 识别:读取文件头魔数 0x7f 'ELF',判断是 32/64 位、可执行/共享库/对象文件。shebang 识别:文件以 #! 开头时,读取其后解释器路径,识别为"脚本"(如 awk script、bash script)。文本编码识别:分析字节模式区分 ASCII、UTF-8、UTF-16、UTF-32、带 BOM 等,并显示 charset。file 还识别压缩包、图片、文档等。常用选项:file file 显示类型,file -i file 显示 MIME 类型与 charset(如 text/plain; charset=utf-8),file -b 简洁输出。file 不依赖文件扩展名,通过内容识别,是判断文件真实类型的可靠工具。

核心是"magic 内容识别"。file 按魔数/shebang/字节模式识别 ELF、脚本与编码,-i 看 MIME 与 charset。

file /bin/ls            # ELF 64-bit ...
file -i script.sh       # text/x-shellscript; charset=utf-8
file data.bin
#

61. hexdump、xxd、od 在二进制分析时各自的特性

hexdump、xxd、od 在二进制分析时各自的特性是什么?

  • 三个十六进制工具
  • 输出格式与用途
  • 选型

hexdump(util-linux)功能强大,支持自定义格式(-C 规范十六进制+ASCII、-e 自定义格式),适合详细二进制分析;xxd 是 vim 自带工具,输出十六进制+ASCII(默认),支持 -r 反转(十六进制转回二进制)、-i 生成 C 数组,适合快速查看与编辑、与 vim 配合;od(octal dump)是 POSIX 标准工具,默认八进制输出,支持 -x(十六进制)、-c(字符)、-d(十进制),兼容性好、跨平台。特性:hexdump 格式自定义最强、xxd 反转方便、od 标准兼容。选型:快速查看用 xxd(或 od -x),需要自定义格式/偏移用 hexdump,跨平台脚本用 od。都可用于查看二进制文件头、魔数、乱码。xxd -r 能把 hex 转回二进制,做文件修复。

核心是"格式自定义 vs 反转 vs 标准兼容"。hexdump 自定义最强、xxd 可反转、od 标准兼容。

hexdump -C file.bin
xxd file.bin
xxd -r hex.txt > out.bin
od -Ax -tx1 file.bin
#

62. lsattr 的常见属性位 a、c、d、i、j、s、t、u 的工程含义

lsattr 的常见属性位 a、c、d、i、j、s、t、u 的工程含义是什么?

  • 各属性位语义
  • 应用场景
  • chattr 设置

lsattr 显示文件属性位(ext 系列)。a(append-only):只能追加不能改写/删除;c(compress):文件自动压缩;d(no dump):dump 备份时跳过;i(immutable):不可变,不可改/删/链接;j(data journaling):数据也写入日志;s(secure delete):删除时安全清零;t(no tail-merging):禁止尾块合并(用于 HFS 兼容);u(undelete):删除后允许恢复(不当 u 恢复)。工程含义:i 用于防篡改关键文件(但需注意影响系统写入),a 用于只追加日志,d 用于备份排除,s 用于安全删除,u 用于可恢复删除。注意:这些属性受文件系统支持限制(ext4 支持较多,xfs/btrfs 部分支持,NFS 不支持),且 s/u 在 ext4 上默认不支持。用 chattr + 设置,lsattr 查看。

核心是"属性位语义与支持"。i 防篡改、a 只追加、d 备份排除、s 安全删除、u 可恢复,但受文件系统支持限制。

lsattr /etc/passwd
chattr +a /var/log/app.log
chattr +i /etc/hosts
chattr +d /backup
#

63. lsof -i、ss -tulpn、netstat 在新版本系统中为何逐渐被 ss 取代

lsof -i、ss -tulpn、netstat 在新版本系统中为何逐渐被 ss 取代?

  • ss 与 netstat 的实现差异
  • 性能与功能
  • 取代原因

netstat 通过读取 /proc/net 和遍历 /proc//fd 收集信息,在大连接数(高并发)下性能差、慢;ss(socket statistics,iproute2 工具)直接读取内核 socket 信息,使用 netlink 与内核通信,性能远优于 netstat,尤其适合大量连接场景。lsof -i 也需遍历 /proc,慢且资源占用高。取代原因:一是性能(ss 快,适合高并发);二是功能(ss 提供更多 socket 状态、filter、更丰富的输出);三是 iproute2 是网络工具集,netstat 属 net-tools 包已停止积极维护。因此新版本系统推荐 ss 与 lsof 组合(ss 看连接,lsof 看进程文件占用)。但 lsof 在查"进程打开的文件"仍有独到价值,ss 侧重网络连接。

核心是"性能与实现"。ss 用 netlink 直接读内核、快,netstat/lsof 遍历 /proc、慢,高并发下 ss 明显占优。

ss -tulpn
ss -s
netstat -tulpn
lsof -i :8080
#

64. rename 与 mv 在批量重命名时如何选择,为什么 mv 不能跨设备

rename 与 mv 在批量重命名时如何选择?为什么 mv 不能跨设备?

  • rename 与 mv 的用途
  • mv 跨设备限制
  • 选型

mv 是单个文件/目录的移动与重命名,可跨目录;rename 专用于批量重命名(按规则替换多个文件)。选型:单个文件改名/移动用 mv,批量按规则改名用 rename(perl 或 util-linux)。mv 不能跨设备的原因:移动文件本质是修改目录项(链接),跨设备(不同文件系统)时无法仅改目录项,需先拷贝数据再删除原文件(mv 会自动降级为 复制+删除),因此跨设备 mv 会实际复制数据、耗时,且若条件不足会失败/部分残留。跨设备强行 mv 性能差且有数据复制;跨设备应用 cp + rm 或 rsync。同设备 mv 是原子性的(仅改目录项),快且无数据复制。

核心是"相同设备 vs 跨设备"。同设备 mv 改目录项原子、快;跨设备需复制+删除、慢;rename 批量改名。

mv file.txt /tmp/          # 同设备快
mv /data/big /mnt/other/   # 跨设备:复制+删除
rename 's/\.txt$/.log/' *.txt
#

65. script 与 asciinema 在录屏时的文件大小与可检索性差异

script 与 asciinema 在录屏时的文件大小与可检索性差异是什么?

  • script 的录屏格式
  • asciinema 的格式
  • 大小与可检索性

script 是传统终端录屏工具,把会话输出记录到 typescript 文件(含控制字符与时间信息,配合 -t 记录时间戳),文件为纯文本但含 ANSI 控制序列,可检索(文本可 grep),但含大量控制字符、回放需 scriptreplay 且对复杂输出(交互、全屏、颜色)兼容一般。asciinema 是专用录屏工具,使用 JSON 格式(.cast)记录时间戳与帧,文件小、可精确回放(asciinema play)、可上传到 asciinema.org 分享,支持逐帧渲染,但对"纯文本 grep"不友好(是 JSON)。差异:script 文件是文本(可 grep 检索但体积大、含控制字符),asciinema 是 JSON(体积小、回放精确但检索需解析)。选型:需要文本检索/日志分析用 script(或直接 tee),需要精确回放/分享/短视频用 asciinema。

核心是"文本可检索 vs JSON 精确回放"。script 文本可 grep 但大,asciinema JSON 小且回放精确但检索需解析。

# script 录屏
script -t typescript 2> timing.log
scriptreplay timing.log typescript
# asciinema 录屏
asciinema rec demo.cast
asciinema play demo.cast
#

66. script、ttyrec、asciinema 录制终端会话的差异与回放

script、ttyrec、asciinema 录制终端会话的差异与回放方式是什么?

  • 三个录屏工具
  • 格式与回放
  • 选型

script(util-linux):把终端输出记录到 typescript,配合 -t 记录时间戳,用 scriptreplay 回放,格式为文本+控制序列,适合简单记录与日志取证。ttyrec:专门录制终端会话,输出 ttyrec 文件(含时间戳头),用 ttyplay 回放,文件含时间信息,适合还原按键与输出时序。asciinema:现代 JSON 格式(.cast),记录时间戳与帧,asciinema play 回放,可上传分享,体积小、回放精确。差异:三者都记录"终端会话",但格式与回放工具不同:script 文本+scriptreplay,ttyrec 专用格式+ttyplay,asciinema JSON+asciinema play。选型:需要文本检索/取证用 script;需要精确按键时序回放用 ttyrec;需要清晰分享/体积小用 asciinema。安全上录屏内容可能含敏感信息,需脱敏。

核心是"格式与回放工具不同"。script 文本+scriptreplay、ttyrec 专用+ttyplay、asciinema JSON+asciinema play。

script -t typescript 2> timing.log; scriptreplay timing.log typescript
ttyrec session.tty; ttyplay session.tty
asciinema rec session.cast; asciinema play session.cast
#

67. stat、file、file -i 与 hexdump 在判断文件类型与编码时各自用途

stat、file、file -i 与 hexdump 在判断文件类型与编码时各自用途是什么?

  • stat 查看元数据
  • file 判断类型
  • hexdump 看原始字节

stat 显示文件元数据(inode、权限、时间戳、大小、类型),不判断内容类型;file 通过 magic 识别文件实际类型(ELF、文本、压缩等);file -i 显示 MIME 类型与字符集(如 text/plain; charset=utf-8)用于判断编码;hexdump 显示原始字节(十六进制),用于人工查看文件头/魔数/编码细节。用途分工:查元数据(权限、时间、大小)用 stat;想知道"这是什么文件"用 file;想知道字符编码/ MIME 用 file -i;需要看二进制内容/魔数/乱码原因用 hexdump。组合:先 file 判断类型,再 hexdump 看文件头魔数确认,stat 看属性。文件类型判断以 file 为准(不看扩展名),编码以 file -i 的 charset 为准。

核心是"元数据 vs 类型 vs 编码 vs 原始字节"。stat 元数据、file 类型、file -i 编码/MIME、hexdump 原始字节。

stat file
file file
file -i file
hexdump -C -n 16 file
#

68. tmpfs、devpts、proc、sysfs 的默认挂载参数错误引发的故障

tmpfs、devpts、proc、sysfs 的默认挂载参数错误会引发什么故障?

  • 四个虚拟文件系统的挂载参数
  • 参数错误的影响
  • 故障排查

tmpfs 挂载点(/dev/shm、/tmp)默认大小有限(如 size=50%),参数错误(如 size 设太小、mode 错误)会导致共享内存/临时文件不足(应用报"not enough space");devpts 用于 PTY,默认挂载点 /dev/pts 需 gid/mode 正确,参数错误会导致 pts 设备不可用、无法打开终端(ssh/tmux 报错);proc 挂载 /proc,参数错误(如 hidepid 设置不当)会导致看不到进程或隐私问题;sysfs 挂载 /sys,参数错误可能导致设备/驱动信息不可见。故障常见于容器/ chroot 中手动挂载这些文件系统时参数遗漏。排查:检查挂载点是否挂载、mount | grep 看参数、容器内 mount -t proc proc /proc 等。正确挂载:tmpfs 设 size、devpts 设 gid=5,mode=620、proc/sysfs 用默认。

核心是"虚拟文件系统挂载参数影响特定功能"。tmpfs 大小/权限、devpts gid/mode、proc/sysfs 挂载即用,参数错引发相应故障。

mount -t tmpfs -o size=1G tmpfs /dev/shm
mount -t devpts -o gid=5,mode=620 devpts /dev/pts
mount -t proc proc /proc
mount -t sysfs sysfs /sys
#

69. 为什么大文件场景应避免使用 vim 直接编辑,如何用 split/tee/sponge 改写

为什么大文件场景应避免使用 vim 直接编辑?如何用 split/tee/sponge 改写?

  • vim 编辑大文件的内存问题
  • head/tail 截取
  • tee/sponge 原子改写

vim 直接编辑超大文件(GB 级)会一次性加载整个文件到内存,占用极大内存、交互卡顿、易崩溃,且 vim 的交换文件/undo 对大文件开销大。应避免直接用 vim 编辑大文件。改写方案:一是用 head/tail/sed/awk 按需提取或修改再重定向;二是用 split 把大文件按行/大小切分(split -l 100000 big.txt part_),分别处理后再 cat 合并;三是用 tee 中间管道或 sponge(moreutils)原子写回——command | sponge file 把命令输出原子写回文件,避免"读大文件同时写自身"导致截断/损坏;四是纯文本替换用 sed -i 或 perl。关键:大文件改写避免"整个读入内存 + 原地写",用流式处理 + 原子写回(sponge/sed -i)。

核心是"内存与原子写"。vim 整文件加载内存不适合大文件,用 split 切分、sed/awk 流式、sponge 原子写回避免损坏。

split -l 100000 big.txt part_
# 流式处理 + 原子写回
sed 's/a/b/g' big.txt | sponge big.txt
# 或 tee 中间处理
awk '{print $1}' big.txt | sponge out.txt
#

70. 为什么大目录 rm -rf 会卡顿,如何用 rsync --delete 替代

为什么大目录 rm -rf 会卡顿?如何用 rsync --delete 替代?

  • rm -rf 大目录的耗时
  • rsync --delete 清空目录
  • 替代方案

rm -rf 大目录(含海量小文件)会卡顿:rm 逐个删除文件,每个文件都要更新目录项与 inode,产生大量元数据操作,删除速度慢、占用大量 CPU/IO,且 delete 时文件系统同步(fsync)可能阻塞。用 rsync --delete 替代:rsync -a --delete empty/ target/ 把空目录同步到 target,rsync 的 --delete 会删除 target 中多余文件,但 rsync 按目录批量删除,配合 --delete 与空目录,删除速度比 rm 快(rsync 用更高效的目录遍历删除)。实际更快的做法:直接 rm -rf 前先 mv dir /tmp/ 再后台删,或 rsync -a --delete 空目录对拷。对海量文件的目录,用 rsync --delete 空目录法(或 find -delete 逐步)可减少卡顿。注意 rsync --delete 需谨慎(会删目标多余文件)。

核心是"元数据删除开销"。rm 逐个删慢,rsync --delete 空目录对拷用更高效目录删除,减少卡顿。

mkdir /tmp/empty
rsync -a --delete /tmp/empty/ /data/large/
# 或先移走再后台删
mv /data/large /tmp/ && rm -rf /tmp/large &
#

71. 大循环中频繁 cat 文件的优化手段,read -r 与 while read 行尾空格问题

大循环中频繁 cat 文件的优化手段是什么?read -r 与 while read 处理行尾空格的问题?

  • 循环内 cat 的优化
  • read -r 与转义
  • while read 行尾空格

大循环中频繁 cat 文件(或反复读文件)会反复打开/读取文件,性能差。优化:一是把文件读入内存一次(data=$(cat file)mapfile/readarray),循环内处理变量而非重新 cat;二是避免循环内调用外部命令(cat/grep/sed),用 bash 内建或一次管道处理;三是用 mapfile/readarray 读入数组再循环。read -r 问题:read -r 禁用反斜杠转义,避免 \n 等被解释,是处理原始行的推荐方式(不加 -r 时 \ 会被吃掉)。while read 行尾空格问题:默认 read 会去除行尾的反斜杠与保留空白可能有歧义;IFS= read -r line 用空 IFS 保留行首尾空格(默认 IFS 会 trim 首尾空白)。正确写法 while IFS= read -r line; do ...; done < file 保留行尾空格且不解释转义。

核心是"减少重复读文件 + read 的正确用法"。循环外读入内存、避免循环内 cat;read -r 禁转义、IFS= 保留行尾空格。

# 优化:一次读入数组
mapfile -t lines < file
for l in "${lines[@]}"; do ...; done
# 正确处理行尾空格
while IFS= read -r line; do
  echo "$line"
done < file
#

72. 大文件复制的工具选型中 cp 与 rsync 在断点续传、校验与带宽控制上的差异,以及 dd 在裸设备复制中的适用场景

大文件复制的工具选型:cp 与 rsync 在断点续传、校验与带宽控制上的差异,以及 dd 在裸设备复制中的适用场景是什么?

  • cp 与 rsync 的复制能力
  • 断点续传/校验/带宽
  • dd 裸设备复制

cp 简单复制,无断点续传、无校验、无带宽控制,中途中断需重来;rsync 支持断点续传(--partial)、校验(--checksum)、带宽控制(--bwlimit)、增量(只传变化)、压缩,适合大文件/网络传输,且可续传。dd 按块底层复制,适合裸设备/分区/扇区级复制(如 dd if=/dev/sda of=/dev/sdb、克隆磁盘、备份 MBR),能做精确块复制,但无校验/断点续传,需注意块大小(bs)与 conv 选项。选型:本地简单复制用 cp;大文件/网络/断点续传/带宽控制用 rsync(--partial --bwlimit --checksum);裸设备/分区/磁盘克隆用 dd(或 ddrescue)。dd 也用于制造大文件、压力测试。注意 rsync 跨设备也需小心。

核心是"按场景选工具"。cp 简单、rsync 断点续传/校验/带宽、dd 裸设备块级复制。

cp bigfile /dest/
rsync -P --bwlimit=5000 --checksum bigfile /dest/
dd if=/dev/sda of=/dev/sdb bs=4M status=progress
#

73. 文件删除后空间未释放的常见原因,lsof 与 fuser 如何排查

文件删除后空间未释放的常见原因是什么?lsof 与 fuser 如何排查?

  • 已删除但被占用文件
  • lsof 定位占用进程
  • fuser 排查

文件删除后空间未释放的常见原因:有进程仍持有该文件的打开句柄(文件被 unlink 但 inode 仍被进程引用),磁盘空间要等句柄关闭(进程退出)才释放。常见于日志文件被 rm 但 tail/应用仍持有,表现为 df 空间仍满但目录里找不到该文件。排查:lsof 找占用已删除文件的进程:lsof +L1 | grep deletedlsof | grep deleted,列出仍持有已删文件的进程与 fd;fuser 按文件/路径定位占用进程(fuser -v /pathfuser -k 杀进程)。处理:关闭占用进程(重启应用)或 > /proc/<pid>/fd/<n> 清空 fd 指向的文件,释放空间。预防:日志用 logrotate 的 copytruncate 或正常 rotate 让应用重开文件,避免长期持句柄。

核心是"deleted 但被占用(inode 仍存)"。lsof +L1 | grep deleted 找占用进程,关闭进程或清空 fd 释放空间。

lsof +L1 | grep deleted
fuser -v /var/log/app.log
# 释放:清空某进程 fd 指向的文件
: > /proc/<pid>/fd/<n>