受够了 xargs?试试 bashumerate

I wrote an bash enumerator because I was sick of xargs

你是否也厌倦了在 for 循环、find 命令和 xargs 之间反复横跳,只为处理文件名里的空格或特殊字符?我受够了这些繁琐且易错的语法,于是亲手打造了 bashumerate。这是一个专为 Shell 设计的可编程迭代器,用统一的 {} 占位符和简洁的语法,轻松遍历文件、行、范围甚至自定义列表。它支持 NUL 分隔确保绝对安全,内置模板模式,甚至允许你通过简单的函数定义扩展新的数据源。无需复杂的配置,只需几行代码,就能让 Shell 脚本变得优雅高效。

这些方法虽然都能用,但各有各的语法、标志和怪癖,我想要一种更简单、更一致的方式来遍历任何内容。
  1. account42

    > 语法一致 —— 文件、行、范围或列表都使用相同的 {} 占位符

    原生 bash 方法的语法不一致,所以这又多了一种需要学习的语法。

    > 模板模式 —— 单引号命令可作为 shell 模板:enumerate -f '*' -- 'cat {} | head -5'

    将命令作为单个字符串传递是个坏主意。现在你不得不考虑转义和引号的问题。如果枚举项包含不安全字符,{} 会被替换成什么?它是只能作为更大的参数的一部分,还是只能单独使用?谁也不知道,因为它不是 bash。与 bash 循环相比,它始终是一个子 shell,这带来了所有相关的含义——甚至 find 也可以管道输出到普通的 bash 循环中。

    > 过滤器 —— 使用 glob 模式的 --include 和 --exclude

    这只是 find 或原生循环提供功能的一小部分。而且既然它通常无法替代这些工具,enumerate 就成了你需要额外学习的东西。

    > 可扩展性 —— 在 lib/enumerators/ 中放入一个文件即可添加自定义源

    扩展 bash 循环甚至不需要 root 权限,你只需将代码添加到循环中即可。

  2. zxexz

    我不想在这里泼冷水,但这个仓库的结构以及文档的冗长和措辞感觉有 80% 是“氛围编程”(vibecoded)的产物。这并不是说这有什么错;我只是觉得自己居然点了进来,有点太轻信了。

    人们使用 xargs 或 parallel 的次数没几次,就会记住一些让他们头疼的怪癖。然后他们会变得谨慎。接着这就变成了肌肉记忆。如果这不是经常发生的事,他们就会学会去查 man 手册。

    总之,在一个“氛围编程”盛行的世界里,当 LLM 们已经基于我们在 Stack Overflow 上发布的所有抱怨进行了训练时,为什么还要往混合体里再添加另一个“工具”呢?

  3. giov4

    我笑喷了,但你觉得这个方案有效吗?

    https://github.com/wallach-game/bashumerate/pull/1/files

    在我看来这是一个正确的解决方案,尽管我也通常厌倦了手写 for 循环。

  4. IdiotSavage

    在这个讨论串里:拥有深奥知识的 bash 专家,无意中展示了 bash 是多么糟糕。

    显而易见的解决方案是使用更理智的东西,比如 PowerShell 或 nushell,但老专家们总是捍卫他们磨练多年的技能,同时批评任何不同的东西。

  5. eqvinox

    我不太明白这个工具的意义。记住一个工具的一堆选项,难道比记住一堆工具/结构更好吗?尤其是如果后者在其他地方也很有用的话。而且除非你开始把它打包分发,否则它无法用于脚本,这……算了吧。

    直接用这个吧:

    foo | while read X; do bar "$X"; done

  6. dundarious

    如果你想用 shell 与结果交互,当然可以直接使用(子)shell。

    ls -1 ./*.sh | xargs -rd\\n sh -c 'for i in "$@" ; do ... ; done' sh

    1. 严格来说不需要使用 -1,因为我相信所有常见的 ls 都能检测到 !isatty(stdout) 并无论如何都会逐行输出。

    2. xargs -r 表示如果没有输入就不运行命令,虽然严格来说也不是必须的,但我已经上瘾了,因为对我来说这是合理的默认设置。

    3. xargs -d\\n 使其将字段收集为整行,这通常是你想要的,除非你能生成 NUL 字符。

    4. 当然可以使用你想要的任何 shell,但我默认不使用 bash 特有的语法,/bin/sh 对我来说就足够了,即使是 dash。

    5. 末尾的 "sh" 是由于 `sh -c` 用法的一个怪癖,其中 $0 是第一个非选项参数,所以 `printf %s\\n 1 2 3 | xargs -rd\\n sh -c 'for i in "$@" ; do printf "%s " "$i" ; done ; echo'`(注意末尾没有 "sh")只会打印 "2 3 ",因为 $0 不包含在 "$@" 中($0 是 1,$1 是 2,$2 是 3)。最简单的方法就是总是手动将 shell 名称本身作为 $0 传入,而不是试图将 "$0" 纳入你的逻辑中。

    当然,你也可以使用 `find . -maxdepth 1 -type f -name '*.sh' -print0 | xargs -r0 ...`,具体取决于你在做什么,这可能是最简单的。只要你的 xargs 支持 -0,这绝对是简单的。

  7. db48x

    > 或者也许你管道输出到 xargs,然后祈祷你的文件名里没有空格……

    永远使用 -0。大多数 gnu 工具都支持它。它会让工具在每个文件名后放置一个空字节而不是换行符。完全消除了处理文件名中空白字符的问题。

  8. tester457

    关于 xargs 替代方案的话题,我非常喜欢 gnu parallel。

    parallel 的 --dry-run 标志让我有信心进行比以往使用 xargs 时更多的批处理。

    Parallel 几乎为每件事都有一个选项,甚至有点太多了。

    但我已经四处寻找过替代品。

    创建者 Ole Tange 维护了一篇极其详尽的文章,列出了各种替代品及其差异。[0]

    gnu parallel 的书籍和阅读材料 [1] 也非常出色。

    [0] https://www.gnu.org/software/parallel/parallel_alternatives....

    [1] https://www.gnu.org/software/parallel/#Tutorial

同日更多故事

2026-07-20