我一次又一次地遇到这个问题:我有一个 glob,它与正确的文件完全匹配,但导致Command line too long
. 每次我都将它转换为find
和 的某种组合时,grep
这适用于特定情况,但这不是 100% 等效的。
例如:
./foo*bar/quux[A-Z]{.bak,}/pic[0-9][0-9][0-9][0-9]?.jpg
Run Code Online (Sandbox Code Playgroud)
是否有将 glob 转换为find
我不知道的表达式的工具?或者是否有find
匹配 glob 而不匹配子目录中相同 glob的选项(例如foo/*.jpg
,不允许匹配bar/foo/*.jpg
)?
mur*_*uru 15
如果问题是您收到参数列表太长错误,请使用循环或内置 shell。虽然command glob-that-matches-too-much
可以出错,for f in glob-that-matches-too-much
但不会出错,所以你可以这样做:
for f in foo*bar/quux[A-Z]{.bak,}/pic[0-9][0-9][0-9][0-9]?.jpg
do
something "$f"
done
Run Code Online (Sandbox Code Playgroud)
循环可能会非常缓慢,但它应该可以工作。
或者:
printf "%s\0" foo*bar/quux[A-Z]{.bak,}/pic[0-9][0-9][0-9][0-9]?.jpg |
xargs -r0 something
Run Code Online (Sandbox Code Playgroud)
(printf
在大多数 shell 中内置,以上可以解决execve()
系统调用的限制)
$ cat /usr/share/**/* > /dev/null
zsh: argument list too long: cat
$ printf "%s\n" /usr/share/**/* | wc -l
165606
Run Code Online (Sandbox Code Playgroud)
也适用于 bash。我不确定这到底是在哪里记录的。
Vimglob2regpat()
和 Pythonfnmatch.translate()
都可以将 glob 转换为正则表达式,但也都使用.*
for *
,匹配/
.
find
(对于-name
/-path
标准谓词)像{a,b}
glob一样使用通配符模式(请注意,这不是 glob 运算符;展开后,您会得到两个 glob)。主要区别在于斜杠的处理(以及在 中没有特别处理的点文件和目录find
)。*
在 globs 中不会跨越多个目录。*/*/*
将导致最多 2 个级别的目录被列出。添加 a-path './*/*/*'
将匹配至少 3 级深度的任何文件,并且不会停止find
列出任何深度的任何目录的内容。
对于那个特别
./foo*bar/quux[A-Z]{.bak,}/pic[0-9][0-9][0-9][0-9]?.jpg
Run Code Online (Sandbox Code Playgroud)
几个 globs,很容易翻译,你想要深度 3 的目录,所以你可以使用:
find . -mindepth 3 -maxdepth 3 \
\( -path './foo*bar/quux[A-Z].bak/pic[0-9][0-9][0-9][0-9]?.jpg' -o \
-path './foo*bar/quux[A-Z]/pic[0-9][0-9][0-9][0-9]?.jpg' \) \
-exec cmd {} +
Run Code Online (Sandbox Code Playgroud)
(或-depth 3
一些find
实现)。或者POSIXly:
find . -path './*/*/*' -prune \
\( -path './foo*bar/quux[A-Z].bak/pic[0-9][0-9][0-9][0-9]?.jpg' -o \
-path './foo*bar/quux[A-Z]/pic[0-9][0-9][0-9][0-9]?.jpg' \) \
-exec cmd {} +
Run Code Online (Sandbox Code Playgroud)
这将保证那些*
和?
无法匹配的/
字符。
( find
,与 globs 相反,它会读取foo*bar
当前目录中除目录之外的目录的内容¹ ,而不是对文件列表进行排序。但是,如果我们抛开与无效字符匹配的内容[A-Z]
或*
/的行为的问题?
是未指定,您将获得相同的文件列表)。
但无论如何,正如@muru 所表明的那样,find
如果只是将文件列表拆分为多个运行以解决execve()
系统调用的限制,则无需求助。一些像zsh
(with zargs
) 或ksh93
(with command -x
) 这样的 shell 甚至有内置的支持。
与zsh
(其 glob 也具有等效于-type f
和大多数其他find
谓词),例如:
autoload zargs # if not already in ~/.zshrc
zargs ./foo*bar/quux[A-Z](|.bak)/pic[0-9][0-9][0-9][0-9]?.jpg(.) -- cmd
Run Code Online (Sandbox Code Playgroud)
((|.bak)
是一个 glob 运算符与{,.bak}
, (.)
glob 限定符等效于find
's -type f
,oN
在那里添加以跳过排序find
,D
以包含点文件 (不适用于此 glob))
¹ 要find
像 globs 一样抓取目录树,您需要以下内容:
find . ! -name . \( \
\( -path './*/*' -o -name 'foo*bar' -o -prune \) \
-path './*/*/*' -prune -name 'pic[0-9][0-9][0-9][0-9]?.jpg' -exec cmd {} + -o \
\( ! -path './*/*' -o -name 'quux[A-Z]' -o -name 'quux[A-Z].bak' -o -prune \) \)
Run Code Online (Sandbox Code Playgroud)
即修剪除 1 级目录以外的所有目录,修剪foo*bar
除quux[A-Z]
or目录外的所有 2级目录,quux[A-Z].bak
然后选择pic...
级别 3 的目录(并修剪该级别的所有目录)。