概括:
- 给定的系统有很多带有名称的文本文件
~=
[type of file].[8-digit date]
。 - 为了搜索这些文件,我喜欢(并且想要保留)使用这个惯用法:(
find /path/ -name 'file.nnnn*' -print | xargs -e fgrep -nH -e 'text I seek'
其中nnnn
== 4 位年份) - ...在过去的十年中,我也
find
跨年进行了全球合作,例如find /path/ -name 'file.201[89]*' -print | xargs ...
- ...但现在我无法
find
在 2019 年和 2020 年使用find /path/ -name 'file.20{19,20}*' -print | xargs ...
- ...尽管“大括号通配符”(正确的术语?)可以很好地工作
ls
!
有没有一种{简洁,优雅}的方式来告诉find
我想要什么,而不需要进行后期find
清理(即我现在正在做的事情)
find /path/ -name 'file.*' -print | grep -e '\.2019\|\.2020' | xargs ...
? FWIW,我更喜欢一个与xargs
.
细节:
我工作的系统有很多约定俗成的约定,这些约定早在我之前就已经存在,而且我无法改变。其中之一是,它有很多名称为 的文本文件~=
[type of file].[8-digit date]
,例如woohoo_log.20191230
.当在这些文件中搜索某些给定文本时,我通常(几乎总是)使用习惯用法find ... grep
(通常使用 Emacs' M-x find-grep
)。 (FWIW,这是一个 Linux 系统
$ find --version
find (GNU findutils) 4.4.2
...
$ bash --version
GNU bash, version 4.3.30(1)-release (x86_64-pc-linux-gnu)
如果我愿意的话,我目前缺乏改变其中任何一个的状态。)我经常有点知道手头上的事情的年份范围,因此会尝试限制find
返回的内容(以加快处理速度),例如
find /path/ -type f -name 'file.nnnn*' -print | xargs -e fgrep -nH -e 'text I seek'
其中nnnn
== 4 位数年份。这个 WFM,我喜欢(并且想保留)使用上面的习语......特别是因为我也可以使用它来跨年搜索,例如
find /path/ -type f -name 'file.201[89]*' -print | xargs ...
但新的十年似乎正在打破这个习惯,而且(至少对我来说)最奇怪的是。 (当过去十年发生变化时,我并不在这里。)假设我选择了我想要的文本知道位于 2019 年的文件 && 2020 年的文件中(例如,我可以打开文件并查看文本)。如果我现在这样做
find /path/ -name 'file.20{19,20}*' -print | xargs ...
grep
出乎意料地/令人烦恼地完成with no matches found
,因为
$ find /path/ -name 'file.20{19,20}*' -print | wc -l
0
但如果我这样做
find /path/ -type f -name 'file.*' -print | grep -e '\.2019\|\.2020' | xargs ...
grep
返回预期结果。这很好,但是......嗯......这只是丑陋的,特别是因为这个“大括号 glob”(如果此用法不正确或以其他方式被弃用,请纠正我)的工作原理ls
!即,这显示了相关年份范围内的文件(即 2019..2020)
ls -al /path/file.20{19,20}*
因此我想知道:
- 我只是没有
find
为这个用例提供正确的 glob 吗?我需要告诉find
它什么才能让它做ls
有能力/正确地做的事情? - 这是有问题吗
xargs
?如果是这样,我可以接受一个find ... -exec
解决方案,但是......我的大脑在 的情况下工作得更好xargs
,所以如果可能的话,我宁愿坚持下去。 (可以说我是弱智,但是-exec
的语法让我的大脑受伤.)
答案1
使用zsh
,您可以使用递归通配符及其<x-y>
匹配十进制数字范围的通配符运算符:
grep -nHFe 'text I seek' /path/**/file.<2019-2020>*(D-.)
((D)
to 也可以查看隐藏的(D
ot)目录find
;如果您不想要它们,大概您可以省略它,并且-.
限制为常规的文件 ( .
) 在符号链接解析 ( -
) 后被识别。
请注意,它也会匹配 on file.00002020
(因为这是 2019 到 2020 之间的十进制数字),并且像您的方法中的 on 一样,file.20201234
它file.2020
匹配file.<2019-2020>
后面跟着1234
which matches *
。
标准(POSIXsh
和实用程序)方法是:
find /path \( -name 'file.2019*' -o -name 'file.2020*' \) -type f \
-exec grep -Fne 'text I seek' /dev/null {} +
(其中添加的效果与GNU强制显示文件名的/dev/null
效果相同)grep
-H
请注意, 的输出find -print
与 的预期输入格式不兼容xargs
。使用 GNU 实用程序,您可以使用find -print0
和xargs -r0
,但这不是必需的,因为find -exec ... {} +
具有相同的行为,更短且更便携。
答案2
在 中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'
在find /path/ -name 'file.20{19,20}*'
,中'file.20{19,20}*'
被引用,因此 shell 不理会它,然后find
应用它自己的模式匹配规则,不支持大括号扩展。这里引用了GNUfind
手册:
模式 ('
{}
') 内的大括号不被视为特殊(即find . -name 'foo{1,2}'
匹配名为 的文件foo{1,2}
,而不是文件foo1
和foo2
.
如果你确实想使用大括号扩展来递归搜索目录,在 bash 中,你可以启用递归通配(globstar
) (并且可能dotglob
像这样查看隐藏目录find
),并printf
与 一起使用xargs
:
shopt -s globstar
printf "%s\0" /path/**/file.20{19,20}* | xargs -0 ...
或者您可以使用find
with-regex
而不是-name
一些find
实现所支持的。使用 GNU find
:
find /path -regextype posix-extended -regex '.*/file.20(19|20)[^/]*'
答案3
这不是您问题的一般情况答案,但可能有一种简单的方法来做到这一点,具体取决于您拥有多少文件历史记录。我在九月/十月找东西时经常遇到类似的情况。一种简单的解决方法是使用如下所示的 a 模式:
file.20[12][90]*
它并不相同,因为除了 2019 年和 2020 年之外,它还会匹配 2010 年和 2029 年。大概您还没有任何日期为 2029 年的文件。如果您的存档不能追溯到 2010 年,那么这在功能上应该是可以的相等的。