*不*使用 shell 的 Glob 文件

*不*使用 shell 的 Glob 文件

我想列出某个子目录中的文件,但我这样做是作为 docker 容器内部的一部分docker exec,所以我不想费心启动一个我并不真正需要的 shell。是否可以使用简单的命令行工具(而不仅仅是 shell)找到 glob 的所有匹配项?

例如,我当前的调用是bash -l -c 'echo /usr/local/conda-meta/*.json'.是否可以使用常用的工具来简化此操作,从而产生类似的东西globber /usr/local/conda-meta/*.json,这会更简单且重量更轻?

答案1

sh简单且普遍可用。sh是被调用来解析许多语言中的命令行的工具system(cmdline)。许多操作系统,包括一些 GNU 操作系统,已经停止使用bash(GNU shell)来实现,sh因为它变得过于臃肿,无法完成解析命令行和解释 POSIXsh脚本这样简单的事情。

您的bash -l -c 'echo /usr/local/conda-meta/*.json'命令行可能已被调用解释sh。所以也许你可以这样做:

printf '%s\n' /usr/local/conda-meta/*.json

直接地。如果不:

sh -c 'printf "%s\n" /usr/local/conda-meta/*.json'

您也可以find在这里使用。find不进行通配,但它可以报告与 shell 类似的模式匹配的文件名。

LC_ALL=C find /usr/local/conda-meta/. ! -name . -prune -name '*.json'

或者通过一些find实现:

LC_ALL=C find /usr/local/conda-meta -mindepth 1 -maxdepth 1 -name '*.json'

(请注意,LC_ALL=C此处需要*匹配任何字节序列,而不仅仅是那些在当前语言环境中形成有效字符的字节序列,这是一个 shell 构造。如果 shell 不解释该命令行,您可能需要将其更改为env LC_ALL=C find...

与 shell glob 的一些区别:

  • 文件列表未排序
  • 包含隐藏文件(您可以添加! -name '.*'来排除它们)
  • 如果没有匹配的文件,则不会得到任何输出。 glob 有一个错误特征,在这种情况下它们会按原样保留未扩展的模式。
  • 使用第一个(标准)变体,文件将输出为/usr/local/conda-meta/./file.json.
  • 有些 globx*/y/../*z不容易翻译(另请注意在这种情况下目录符号链接的不同行为)。

无论如何,你不能用来echo输出任意数据。

我的下一个问题是:您将如何处理该输出?使用 时echo,您将输出由 SPC 字符分隔的文件路径,而使用 myprintffind以上时,则由 NL 字符分隔。NL和都是SPC文件名中完全有效的字符,因此这些输出在后处理过程中不可靠。您可以使用'%s\0'代替'%s\n'(或使用find's-print0如果支持),不适合向用户显示,但可进行后处理。

在效率方面,将 Ubuntu 20.04 /bin/sh(dash 0.5.10.2)与其find(GNU find4.7.0)进行比较。

启动时间:

$ time (repeat 1000 sh -c '')
( repeat 1000; do; sh -c ''; done; )  0.91s user 0.66s system 105% cpu 1.483 total
$ time (repeat 1000 find . -quit)
( repeat 1000; do; find . -quit; done; )  1.35s user 1.25s system 103% cpu 2.507 total

通配一些json文件:

$ TIMEFMT='%U user %S system %P cpu %*E total'
$ time (repeat 1000 sh -c 'printf "%s\n" /usr/share/iso-codes/json/*.json') > /dev/null
0.95s user 0.72s system 105% cpu 1.587 total
$ time (repeat 1000  find /usr/share/iso-codes/json -mindepth 1 -maxdepth 1 -name '*.json') > /dev/null
1.34s user 1.35s system 103% cpu 2.599 total

Evenbash几乎不比find这里慢:

$ time (repeat 1000 bash -c 'printf "%s\n" /usr/share/iso-codes/json/*.json') > /dev/null
1.53s user 1.36s system 102% cpu 2.808 total

当然,YMMV 取决于系统、实现、相应实用程序的版本以及它们所链接的库。

现在,在历史记录中,全局这个名字实际上来自glob70 年代初 Unix 的第一个版本中调用的一个实用程序的名称。它位于/etc并被调用sh作为扩展通配符模式的助手。

您会在网上找到一些项目来复兴非常古老的 shell,例如https://etsh.nl/。更多地作为考古学的练习,您可以glob从那里构建实用程序,然后能够执行以下操作:

glob printf '%s\n' '/usr/local/conda-meta/*.json'

不过有一些警告。

  • 这些是古老的球体,[!x](更不用说[^x])不受支持。
  • 它不是 8 位安全的。实际上,第8位用于逃跑glob 运算符($'\xe9*'将匹配与 相同的内容i*$'\xaa*'将匹配以 开头的文件名*;shell 会在调用之前为引用的字符设置第 8 位glob
  • 范围,例如[a-f]字节值匹配而不是排序规则(实际上,在我看来,这通常是一个优势)。
  • 不匹配的 glob 会导致No match错误(再次,可能更好的是,这就是破碎的由 70 年代末的 Bourne shell 开发)。

glob后来,从 70 年代末的 PWB shell 和 Bourne shell 开始,该功能被转移到 shell 中。后来,一些fnmatch()函数glob()被添加到 C 库中,以允许其他应用程序使用该功能,但我不知道有一个标准或通用实用程序是该函数的裸接口。甚至在早期perl用于调用来扩展全局模式。csh

答案2

全局文件没有使用外壳

明显需要阅读的文档是全局(7)

您可以编写或使用 C 程序调用匹配(3),全局(3),网络电视(3),统计数据(2),读取目录(3)

如果你输入代码诡计,Python,,,奥卡梅尔,通用语言(例如SBCL) ...你会发现类似的功能。用C++看看波科Qt

我假设您使用的是 Linux 系统。顺便说一句,我的交互式 shell 是桀骜(恕我直言,其自动完成功能更可取)。

相关内容