如何单独处理 ripgrep 输出的路径列表中的每个路径

如何单独处理 ripgrep 输出的路径列表中的每个路径

我使用的是 Linux Ubuntu 18.04 和 20.04。

Ripgrep ( rg) 可以输出包含匹配项的文件的路径列表,如下所示:

# search only .txt files
rg 'my pattern to match' -g '*.txt' -l
# long form
rg 'my pattern to match' --glob '*.txt' --files-with-matches

输出将是:

path/to/file1.txt
path/to/file2.txt
path/to/file3.txt

ETC。

然后,我想在每个路径上运行另一个命令,例如tree $(dirname $PATH),以获取包含匹配文件的目录中所有文件的列表。我怎样才能做到这一点?

我觉得xargs可能是答案的一部分?但是以xargs这样的管道作为开始似乎只能处理最后打印的文件:

rg 'my pattern to match' -g '*.txt' -l | xargs -0 -I {} dirname {}

注意:如果您可以使用grepToo 进行演示,那么对于没有 ripgrep 的人来说可能也很有用ripgrep,尽管是 ripgrep超级容易安装

参考:

  1. ripgrep:仅打印匹配模式的文件名

答案1

在 GNU 系统上,可能是这样的:

rg -g '*.txt' -l0 'my pattern to match' | # list files NUL-delimited
  xargs -r0 dirname -z -- |               # takes dirnames
  LC_ALL=C sort -zu |                     # remove duplicates
  xargs -r0 tree --

请注意,如果 和 都dir/file.txt匹配dir/subdir/file.txt,您最终将tree在 和 上dir运行dir/subdir,因此您将看到两次的内容dir/subdir

您的想法是正确的,使用xargswhich is 命令将字节字符串转换为参数列表以传递给命令,并使用-0which 是传递任意参数列表的最可靠方法,但是:

  • xargs -0期望输入的格式为参数列表由 NUL 字符(0 字节)分隔的格式。您需要-0/--null选项才能rg以该格式打印文件列表。
  • GNUdirname每次调用可以处理多个参数,因此-I{}我们不使用 ,而是将它们全部传递²。我们还希望在文件列表为空时根本-r不调用,并且(也是 GNU 特定的)选项本身打印 NUL 分隔的目录。dirname-zdirnamedirname
  • 由于rg不会为每个文件添加前缀,因此重要的是对我们将文件列表作为参数传递给的命令./使用选项分隔符,以避免文件名中出现前导 s 的问题。---

简而言之,对于其值可以是任何非 NUL 字节序列(例如文件路径或任意命令参数)的列表,您希望使用 NUL 分隔的记录作为交换格式,以编程方式在工具之间传递列表,并且只保留人类格式向用户提供反馈的工具(此处为 的树状输出tree)。


在非 GNU 系统上,但使用zshshell,您可以执行以下操作:

files=( ${(0)"(rg -g '*.txt' -l0 'my pattern to match')"} )
typeset -U unique_dirs=( $files:h )
(( $#unique_dirs )) && tree -- $dirs

或者一口气(假设至少有一个匹配的文件):

tree -- ${(u)${(0)"$(rg -g '*.txt' -l0 'my pattern to match')"}:h}

u( nique u) 取代了typeset -U。参数扩展标志0 是我们如何告诉zsh在 NUL 上进行分割的方式。或者,我们可以设置IFS=$'\0'并依赖分词(在未加引号的参数扩展时完成):

IFS=$'\0'
tree -- ${(u)$(rg -g '*.txt' -l0 'my pattern to match'):h}

如果您既没有 GNU 实用程序也没有zsh,您可以随时求助于perl

rg -g '*.txt' -l0 'my pattern to match' |
  perl -MFile::Basename -MList::Util=uniq  -0 -e '
    @dirs = uniq(map {dirname$_} <>);
    exec "tree", "--", @dirs if @dirs'

¹ 这是唯一不能出现在命令参数中的字符/字节值(因为参数在系统execve()调用中作为 NUL 分隔的字符串传递),但它可以出现在通过管道馈送的字节流中,因此它是一个简单的以及在那里分离任意参数的明显方法。-0是 GNU 实现的非标准扩展xargs,但现在在许多其他实现中都可以找到它

² 或至少一次调用中可以容纳的数量,dirname仅在需要时调用多次。

答案2

更新:新的最终答案:

请注意,sort -zu对以 null 分隔的 ( -z) 列表进行排序并删除重复项。

rg 'my pattern to match' -0 -g '*.txt' -l \
| sort -zu \
| xargs -0 -I{} -- dirname {} \
| xargs -0 -I{} -- tree {}

旧答案详细信息:

请参阅此答案下面的评论。我在这里的回答并不那么有力@Stéphane Chazelas 的另一个答案

我下面的答案最初不能正确处理任何带有空格或其他空格的文件名,也不能处理以破折号(-)开头的文件名。以下是我的回复评论:

@StéphaneChazelas,你所有的评论都有道理。你的回答更加有力。使用--null( -0) withrg和 withxargs肯定会更加稳健。使用--也会。我想我并不太关心这些事情,因为我在 git 存储库中运行此命令,其中没有一个文件包含空格,也没有以破折号 ( -) 开头。至于多个dirname&tree调用而不是具有多个路径的一次调用,我知道这一点,但也同意这一点,部分原因是我想要一个答案,我可以轻松扩展并添加更多管道和命令,而无需彻底更改它。

所以,看看这两个答案。他在技术上更好,但就我的目的而言,我的目前“足够好”,并指出我在问题中的原始示例可以通过超级最小的更改来工作。前任:

# I should have done this (add `-0` to `rg` and add `--` to `xargs`):
rg 'my pattern to match' -0 -g '*.txt' -l | xargs -0 -I {} -- dirname {}

# instead of this:
rg 'my pattern to match' -g '*.txt' -l | xargs -0 -I {} dirname {}

@Stéphane Chazelas 的回答以及我的问题下的评论(其中包括 ripgrep 制作者本人制作的一个!)都很有用,并帮助我找出以下内容,我认为这是最简单和最好的答案,因为它是最简单的:

的输出路径字符串rg不是以 null 结尾的字符串,因此-0xargs命令中删除(或者,相反,rg也将其添加到命令中)。就是这样!现在可以了

# THESE WORK to get the dirnames!
# (`--null`/`-0` are removed from both `rg` and `xargs`)

rg 'my pattern to match' -g '*.txt' -l | xargs -I {} dirname {}
# OR (same thing--remove the space after `-I` is all):
rg 'my pattern to match' -g '*.txt' -l | xargs -I{} dirname {}

或者,您可以通过在命令中添加-0或来强制路径字符串以 null 结尾,因此这也有效:--nullrg

# ALSO WORKS
# (`--null`/`-0` are ADDED to both `rg` and `xargs`; note that for
# both `rg` and `xargs`, `--null` is the long form of `-0`)

rg 'my pattern to match' -g '*.txt' -l --null | xargs --null -I{} dirname {}

tree现在,通过扩展,我们可以像这样传递所有路径:

最终答案:

rg 'my pattern to match' -0 -g '*.txt' -l \
| xargs -0 -I{} -- dirname {} \
| xargs -0 -I{} -- tree {}

就是这样!我只需要要么添加或者减去 -0或者--null来自两个rg和所有xargs调用,以保持它们全部一致并在解析多个路径时期望相同​​的轮廓符。

添加 -0但是, or--null更好,因为这样它允许路径中包含空格或其他空白,并且添加--也很好,因为这样它允许以破折号 ( -) 开头的路径。所以,这就是我上面所做的。

不过,也请参阅其他答案。它还可以排序、删除重复项并处理其他复杂问题。

也可以看看

  1. 更多我的xargs学习和例子:
    1. 如何dos2unix使用多个进程在所需的目录或路径上递归运行(或任何其他命令)
    2. 请参阅xargs我的自述文件中的示例,此处:https://github.com/ElectricRCAircraftGuy/FatFs/tree/main

关键词:如何正确使用xargs;使用 xargs 解析 grep 或 ripgrep rg 输出路径

相关内容