新的十年:如何说出类似`find /path/ -name 'file.20{19,20}*'`(但有效)

Tom*_*che 3 find gnu wildcards

概括:

  1. 一个给定的系统有很多带有名称的文本文件~= [type of file].[8-digit date]
  2. 要搜索这些文件,我喜欢(并想保留)使用以下习语:(find /path/ -name 'file.nnnn*' -print | xargs -e fgrep -nH -e 'text I seek'其中nnnn== 4 位数字年份)
  3. ......在过去的十年里,我也findfind /path/ -name 'file.201[89]*' -print | xargs ...
  4. ...但现在我无法find在 2019 年和 2020 年与find /path/ -name 'file.20{19,20}*' -print | xargs ...
  5. ...虽然“花括号通配符”(正确的术语?)与ls!

是否有一种{简洁、优雅}的方式来告诉find我想要什么,而无需进行后期find清理(​​即我现在正在做的事情)à la

find /path/ -name 'file.*' -print | grep -e '\.2019\|\.2020' | xargs ...
Run Code Online (Sandbox Code Playgroud)

? FWIW,我更喜欢与xargs.

细节:

我在一个有很多约定的系统上工作,这些约定早在我之前并且我无法改变。其中之一是,它有很多带有名称的文本文件~= [type of file].[8-digit date],例如woohoo_log.20191230. 在这些文件中搜索某些给定文本时,我通常(如在,几乎总是)使用find ... grep习语(通常使用 Emacs' M-x find-grep)。(FWIW,这是一个带有

$ find --version
find (GNU findutils) 4.4.2
...
$ bash --version
GNU bash, version 4.3.30(1)-release (x86_64-pc-linux-gnu)
Run Code Online (Sandbox Code Playgroud)

如果我愿意,我目前缺乏更改其中任何一个的状态。)我经常有点知道手头问题的年份范围,因此会尝试限制find返回的内容(以加快处理速度),例如(例如)

find /path/ -type f -name 'file.nnnn*' -print | xargs -e fgrep -nH -e 'text I seek'
Run Code Online (Sandbox Code Playgroud)

其中nnnn== 4 位数字年份。这个 WFM,我喜欢(并想继续)使用上述习语......特别是因为我也可以用它来跨年搜索

find /path/ -type f -name 'file.201[89]*' -print | xargs ...
Run Code Online (Sandbox Code Playgroud)

但是这个新的十年似乎打破了这个习惯,而且(至少对我来说)最奇怪的是。(当过去十年发生变化时,我并不在这里。)假设我选择了我知道在 2019 年的文件中的文本&& 来自 2020 年的文件(例如,我可以打开文件并查看文本)。如果我现在做

find /path/ -name 'file.20{19,20}*' -print | xargs ...
Run Code Online (Sandbox Code Playgroud)

grep意外/恼人地完成with no matches found,因为

$ find /path/ -name 'file.20{19,20}*' -print | wc -l
0
Run Code Online (Sandbox Code Playgroud)

但如果我这样做

find /path/ -type f -name 'file.*' -print | grep -e '\.2019\|\.2020' | xargs ...
Run Code Online (Sandbox Code Playgroud)

grep返回预期的结果。这很好,但是......嗯......这只是丑陋的,尤其是因为这个“花括号球”(如果这种用法不正确或以其他方式被弃用,请纠正我)从ls!即,这向我显示了相关年份范围内的文件(即 2019..2020)

ls -al /path/file.20{19,20}*
Run Code Online (Sandbox Code Playgroud)

因此我想知道:

  1. 我只是没有find为这个用例提供正确的 glob 吗?我需要告诉find什么才能让它做ls有能力/正确做的事情?
  2. 这是问题xargs吗?如果是这样,我可以接受一个find ... -exec解决方案,但是……我的大脑用 更好地工作xargs,所以如果可能的话,我更愿意坚持下去。(叫我弱智,但它-exec的语法让我的大脑受伤。)

Sté*_*las 12

使用zsh,您可以使用递归<x-y>通配符及其匹配十进制数范围的通配符:

grep -nHFe 'text I seek' /path/**/file.<2019-2020>*(D-.)
Run Code Online (Sandbox Code Playgroud)

(D)也可以查看隐藏的(Dot)目录find;如果您不想要它们,大概可以省略它,并且-.限制为在符号链接解析()之后识别的常规文件.-))。

请注意,它也将匹配file.00002020(因为这是 2019 年和 2020 年之间的十进制数),就像您的方法一样,file.20201234因为它的file.2020which 匹配,file.<2019-2020>然后是1234which 匹配*

标准(POSIXsh和实用程序)方法是:

find /path \( -name 'file.2019*' -o -name 'file.2020*' \) -type f \
  -exec grep -Fne 'text I seek' /dev/null {} +
Run Code Online (Sandbox Code Playgroud)

(其中添加/dev/null与 GNUgrep的效果相同-H以强制显示文件名)

请注意, 的输出find -print与 的预期输入格式不兼容xargs。使用 GNU 实用程序,您可以使用find -print0and xargs -r0,但这不是必需的,因为它find -exec ... {} +具有相同的行为,更短且更便携。


mur*_*uru 6

在 中ls -al /path/file.20{19,20}*,它ls与无关{19,20}*。在该命令中,shell 执行大括号扩展通配符/path/file.20{19,20}*因为它没有被引用

bash-5.0$ set -x
bash-5.0$ echo {a,b}
+ echo a b
a b
bash-5.0$ ls {a,b}
+ ls a b
ls: cannot access 'a': No such file or directory
ls: cannot access 'b': No such file or directory
bash-5.0$ find -iname {a,b}
+ find -iname a b
find: paths must precede expression: `b'
Run Code Online (Sandbox Code Playgroud)

find /path/ -name 'file.20{19,20}*','file.20{19,20}*'被引用,所以 shell 不理会它,find然后应用它自己的模式匹配规则,不支持大括号扩展。这里引用GNUfind手册

模式 (' {}')内的大括号不被认为是特殊的(即find . -name 'foo{1,2}'匹配名为 的文件foo{1,2},而不是文件foo1foo2.

如果你真的想用括号展开递归搜索目录,在bash,您可以启用递归通配(globstar)(并可能dotglob寻找到隐藏的目录一样find会),并使用printf具有xargs

shopt -s globstar
printf "%s\0" /path/**/file.20{19,20}* | xargs -0 ...
Run Code Online (Sandbox Code Playgroud)

或者您可以使用findwith-regex而不是-name由一些find实现支持。使用 GNU find

find  /path -regextype posix-extended -regex '.*/file.20(19|20)[^/]*'
Run Code Online (Sandbox Code Playgroud)

  • `\( -name 'file.2019*' -o -name 'file.2020*' \)` 输入会更短,更难出错(可以说),并且更便携。 (6认同)