moreutils:15 个 Unix 命令行缺了三十年的工具,我为什么全装上了

阅读时长:14分钟

你大概写过这样的命令:

sort data.txt | uniq > data.txt

然后发现文件空了。因为 shell 先打开 data.txt 准备写入,把它清空,然后才轮到 sort 去读它——读到的是空文件。

这是 Unix 管道的经典陷阱。三十年来,大家的解法是加临时文件:

sort data.txt | uniq > tmp && mv tmp data.txt

多丑。moreutils 的 sponge 一句话解决:

sort data.txt | uniq | sponge data.txt

TL;DR

  • moreutils 是 Joey Hess 收集的 15 个 Unix 工具,补的是三十年来的设计空白
  • 核心是 sponge:吸收所有 stdin 再一次性写入文件,解决"输出重定向到正在读取的文件"
  • vipe 让你在管道中间插入编辑器,vidir 用编辑器编辑整个目录
  • parallel 并行跑任务,ts 给每行输出加时间戳,chronic 只在失败时输出
  • 实测:parallel 比 xargs -P 快但不如 GNU parallel;sponge 无替代品,必装

moreutils 是什么?

Joey Hess(也是 Debian 开发者)收集的一套命令行工具,官方描述:

“moreutils is a growing collection of the unix tools that nobody thought to write long ago when unix was young.”

翻译过来:Unix 年轻时候没人想到要写,但你现在每天都需要的工具。

15 个工具列表:

工具 作用 一句话场景
chronic 命令成功时静默,失败时才输出 定时任务日志降噪
combine 两个文件做布尔运算(and/or/xor/not) 对比两个用户列表
errno 查询 errno 名称和描述 看报错码含义
ifdata 获取网卡信息,不用解析 ifconfig 脚本里取 IP
ifne stdin 非空时才执行命令 避免空输入触发操作
isutf8 检查文件或 stdin 是否 UTF-8 处理乱码文件前检查
lckdo 持锁执行程序(已弃用) —
mispipe 管道两个命令,返回第一个的退出码 修 pipefail 问题
parallel 并行跑多个任务 批量处理
pee 把 stdin 同时 tee 到多个管道 多路输出
sponge 吸收 stdin 再写入文件 本文主角
ts 给每行 stdin 加时间戳 日志计时
vidir 用文本编辑器编辑整个目录 批量重命名文件
vipe 在管道中间插入编辑器 交互式编辑管道数据
zrun 自动解压参数后传给命令 直接处理压缩包

sponge:Unix 管道的第一号痛点

为什么 shell 原生做不到?

Shell 的重定向是先打开文件,再执行命令。cmd > file 的执行顺序:

  1. Shell 打开 file(清空它)
  2. 执行 cmd
  3. cmd 的输出写入 file

当 cmd 需要读取 file 时,它读到的是已被清空的版本。

sponge 的解法

sponge 先把所有 stdin 全部吸收到内存/临时文件,等上游命令结束后,再一次性写入目标文件。

# 经典场景:原地排序
sort data.txt | uniq | sponge data.txt

# 场景:原地去重
cat access.log | sort -u | sponge access.log

# 场景:原地过滤
grep -v '^#' config.conf | sponge config.conf

内存注意:sponge 会把整个输入读进内存。处理 GB 级文件时小心,它的 --sponge-buffer 选项可改用临时文件缓冲。

实测对比

方案 命令 缺点
临时文件 sort f > t && mv t f 需要额外空间、两步操作
sponge sort f | sponge f 一行,但内存占用等于文件大小
Python lines = sorted(set(open(f).readlines())) 需要写脚本

结论:日常小文件用 sponge,大文件(>1GB)用临时文件或流式处理。

vipe + vidir:编辑器是终极 Unix 工具

vipe:在管道里插入编辑器

# 场景:交互式编辑命令输出后再传给下一步
ls -la | vipe | grep '\.txt$'

vipe 会打开你的 $EDITOR(vim/nano/code),让你手动编辑管道中间的数据,保存后自动传给下游命令。

实用场景:

# 从一堆文件里挑几个要压缩的
find . -name '*.log' | vipe | xargs tar czf logs.tar.gz

# 手动筛选要删除的进程
ps aux | vipe | awk '{print $2}' | xargs kill

踩坑 1:vipe 需要 $EDITOR 已设置。没设置时它会报错或调用默认编辑器。在脚本里用要显式设置:

export EDITOR=vim

vidir:编辑整个目录

vidir 用你的编辑器打开一个目录清单(文件名列表),你改完保存后,它自动执行重命名/移动/删除操作。

# 打开当前目录的文件列表
vidir

# 批量重命名:把 .jpeg 改成 .jpg
# 在编辑器里把每行的 .jpeg 改成 .jpg,保存退出
vidir --editor=vim

对比传统方案:

方案 批量重命名体验
mv 循环 需要写 for 循环,容易出错
rename 功能强大但语法反直觉(Perl 正则)
vidir 直接在编辑器里改文件名,所见即所得

踩坑 2:vidir 删除某行就是删除该文件,没有确认提示。改之前先备份,或者在编辑器里检查一遍。

parallel vs xargs vs GNU parallel

moreutils 的 parallel 用来并行跑任务:

# 并行转换 100 张图片
ls *.png | parallel -j 8 convert {} {.}.jpg

# 对比 xargs
ls *.png | xargs -P 8 -I {} convert {} {.}.jpg

实测对比

方案 语法 速度 功能
moreutils parallel parallel -j N cmd 中 基础并行
xargs -P xargs -P N -I {} 中 无进度条
GNU parallel parallel cmd 快 进度条/断点续跑/远程执行

关键差异:

  • moreutils parallel 和 xargs -P 性能接近,语法不同
  • GNU parallel 功能强得多(进度条、--joblog、--resume),但依赖 Perl,在精简系统(Alpine、部分容器)里可能没装
  • moreutils 的 parallel 是纯 C,体积小,适合放进精简镜像

选型建议:

场景 推荐
精简系统/容器 moreutils parallel
有 Perl 环境 GNU parallel
一次性简单任务 xargs -P

ts:给日志加时间戳

# 给每行输出加时间戳
ping 8.8.8.8 | ts

# 指定格式
ping 8.8.8.8 | ts '%Y-%m-%d %H:%M:%S'

# 测量命令耗时
long_running_cmd | ts

踩坑 3:ts 是缓冲的,它会等上游有输出才加时间戳。如果你想测命令总耗时,用 time;想测每步间隔,用 ts。

chronic:定时任务日志降噪

# 成功时什么都不输出,失败时才输出
chronic rsync -av /data backup:/data

# 在 cron 里
0 3 * * * chronic /usr/local/bin/backup.sh

每天凌晨跑备份,成功时你不会收到邮件,失败时才报警。这是运维标配。

其他工具速查

errno:查错误码

$ errno 2
ENOENT 2 No such file or directory

$ errno EACCES
EACCES 13 Permission denied

combine:文件布尔运算

# 两个列表取交集
combine list1.txt and list2.txt

# 取并集
combine list1.txt or list2.txt

# 取差集
combine list1.txt not list2.txt

ifne:空输入不执行

# 只有找到文件才执行
find . -name '*.tmp' | ifne xargs rm -f

pee:多路输出

# 同时 tee 到两个管道
cat data.txt | pee 'grep foo' 'grep bar'

zrun:自动解压

# 直接处理压缩包里的文件
zrun cat logs.gz

安装方法

# Debian/Ubuntu
sudo apt install moreutils

# macOS (Homebrew)
brew install moreutils

# Fedora/RHEL
sudo dnf install moreutils

# Arch
sudo pacman -S moreutils

# 从源码
git clone https://github.com/pgdr/moreutils.git
cd moreutils && make && sudo make install

成本账本

项目 成本
moreutils 本身 免费开源(GPL)
安装体积 ~1-2 MB(15 个静态二进制)
依赖 无(纯 C,libc 即可)
维护 极低,Joey Hess 长期维护

对比:GNU parallel 需要 Perl(~20MB),moreutils 是 15 个独立小工具,适合放进 Docker 镜像或精简系统。

常见问题

Q1: sponge 和 tee 有什么区别?

A: tee 是实时分流(一边写文件一边写 stdout),sponge 是先全部吸收再一次性写入。tee file 不能用于"读取并覆盖同一文件"的场景,sponge 可以。

Q2: moreutils 的 parallel 和 GNU parallel 冲突吗?

A: 会冲突——两者都叫 parallel。如果同时安装,后装的会覆盖 PATH 里的 parallel。建议只装一个,或者用别名区分。

Q3: vidir 删除了文件能恢复吗?

A: 不能,vidir 删行即删文件,无回收站。改前先 cp -r dir dir.bak 备份。

Q4: vipe 在脚本里能用吗?

A: 能,但需要设置 $EDITOR。脚本里建议 export EDITOR=$(command -v vim)。

Q5: 为什么不直接升级到新版本 Unix?

A: 这些工具的空白不是 Unix 版本问题,是设计哲学问题。Unix 哲学是"做好一件事",sort 就该只排序,不该管"原地覆盖"。moreutils 是补这个组合层的工具。

Q6: moreutils 会持续维护吗?

A: Joey Hess 从 2006 年维护至今,Debian 官方仓库收录。社区活跃,bug 修复及时。

Q7: Windows 能用吗?

A: 原生不能,需要 WSL 或 Cygwin。Windows 用户建议用 WSL2 + apt 安装。

相关阅读

© 2026 softon.top

本站已稳定运行 307 天 9 小时 · 147 篇文章

使用 Hugo 构建 主题 Stack 由 Jimmy 设计 由 softon 魔改

最近构建时间:2026-11-04 17:21:05 CST