我想列出某个子目录中的文件,但我这样做是作为docker exec
docker 容器内部的一部分,所以我不想费心启动一个我并不真正需要的 shell。是否可以使用简单的命令行工具而不只是 shell 找到 glob 的所有匹配项?
例如,我当前的调用是bash -l -c 'echo /usr/local/conda-meta/*.json'
. 是否可以使用常用的工具来简化这个过程,从而得到类似 的东西globber /usr/local/conda-meta/*.json
,它会更简单、重量更轻?
Sté*_*las 16
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
Run Code Online (Sandbox Code Playgroud)
直接地。如果不:
sh -c 'printf "%s\n" /usr/local/conda-meta/*.json'
Run Code Online (Sandbox Code Playgroud)
你也可以find
在这里使用。find
不进行 globbing,但它可以报告匹配类似于 shell 模式的文件名。
LC_ALL=C find /usr/local/conda-meta/. ! -name . -prune -name '*.json'
Run Code Online (Sandbox Code Playgroud)
或者使用一些find
实现:
LC_ALL=C find /usr/local/conda-meta -mindepth 1 -maxdepth 1 -name '*.json'
Run Code Online (Sandbox Code Playgroud)
(请注意,LC_ALL=C
这里需要*
匹配任何字节序列,而不仅仅是那些在当前语言环境中形成有效字符的字节序列,是一个 shell 构造。如果该命令行不被 shell 解释,您可能需要将其更改为env LC_ALL=C find...
)
与 shell globs 的一些区别:
! -name '.*'
以排除它们)/usr/local/conda-meta/./file.json
.x*/y/../*z
的不容易翻译(还要注意在这种情况下与目录的符号链接有关的不同行为)。在任何情况下,都不能用于echo
输出任意数据。
我的下一个问题是:你打算用那个输出做什么?使用echo
,您将输出由 SPC 字符分隔的文件路径,而使用 myprintf
或find
更高版本,则输出由 NL 字符分隔的文件路径。这两个NL
和SPC
是完全在文件名中的有效字符,所以这些输出是没有经过后处理的可靠。您可以使用'%s\0'
代替'%s\n'
(或使用find
's-print0
如果支持),不适合向用户显示,但可后处理。
在效率方面,将 Ubuntu 20.04 /bin/sh
(破折号 0.5.10.2)与其find
(GNU find
4.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
Run Code Online (Sandbox Code Playgroud)
通配一些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
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
当然,YMMV 取决于系统、实现、相应实用程序的版本以及它们所链接的库。
现在在历史记录中,glob名称实际上来自于glob
70 年代初在 Unix 的第一个版本中调用的实用程序的名称。它位于/etc
并被调用sh
作为扩展通配符模式的助手。
您会在网上找到一些项目来恢复非常旧的 shell,例如https://etsh.nl/。更多地作为考古学练习,您可以glob
从那里构建实用程序,然后能够执行以下操作:
glob printf '%s\n' '/usr/local/conda-meta/*.json'
Run Code Online (Sandbox Code Playgroud)
虽然有一些警告。
[!x]
(更不用说[^x]
)不受支持。$'\xe9*'
将匹配与 相同的内容i*
,$'\xaa*'
匹配以 开头的文件名*
;shell 会在调用之前为引用的字符设置第 8 位glob
)[a-f]
匹配字节值而不是排序规则(实际上,这通常是 IMO 的优势)。No match
错误(同样,可能最好是,这是在 70 年代后期被 Bourne shell破坏的东西)。该glob
功能后来从 70 年代后期的 PWB shell 和 Bourne shell 开始移入 shell。后来,一些fnmatch()
和glob()
函数被添加到 C 库中,以允许从其他应用程序中使用该功能,但我不知道标准或通用实用程序是该函数的裸接口。甚至perl
用于csh
在其早期调用来扩展全局模式。
不使用 shell 的Glob 文件
要阅读的明显文档是glob(7)。
您可以编写或使用 C 程序调用fnmatch(3)、glob(3)、nftw(3)、stat(2)、readdir(3)
如果您使用Guile、Python、Go、Rust、Ocaml、Common Lisp(例如SBCL)编写代码……您会发现类似的函数。使用 C++ 查看POCO和Qt。
我假设您使用的是 Linux 系统。顺便说一句,我的交互式 shell 是zsh(恕我直言,它的自动完成功能更可取)。
归档时间: |
|
查看次数: |
1286 次 |
最近记录: |