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

など。

tree $(dirname $PATH)次に、各パスで異なるコマンドを実行して、一致するファイルを含むディレクトリ内のすべてのファイルのリストを取得したいと思います。どうすればいいですか?

xargsそれが答えの一部になるかもしれませんか?しかし、xargsこのようなパイプラインで始めると、最後に印刷されたファイルだけを処理するようです。

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

注:Tooを使用してデモンストレーションできる場合は、ripgrepを持ってgrepいない人にも役立ちます。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内容は2回表示されます。treedirdir/subdirdir/subdir

あなたの考えは正しいです。xargswhich isコマンドを使用してバイト文字列をパラメータリストに変換してコマンドに渡し、-0whichを使用すると、任意のパラメータリストを渡す最も信頼性の高い方法になります。

  • xargs -0入力は、パラメーター・リストがNUL文字(0バイト)で区切られた形式であると予想されます。この形式でファイルリストを印刷するには、-0/オプションが必要です。--nullrg
  • GNUはdirname呼び出しごとに複数の引数を処理できるため、-I{}これを使用する代わりにすべての引数を渡します。また、ファイルのリストが空の場合、それをまったく呼び出さずに-rNULで区切られたディレクトリを印刷するオプション自体もGNUに対応します。dirname-zdirnamedirname
  • 各ファイルにはプレフィックスが付いていないため、ファイル名の前に s が付く問題を回避するために、ファイルのリストを引数として渡すコマンドにrgオプションの区切り./文字を使用することが重要です。---

簡単に言うと、値がNULではなくバイトシーケンス(ファイルパスや任意のコマンド引数など)になる可能性があるリストの場合、NULで区切られたレコードを交換形式で使用するツールとツールの間でプログラムでリストを渡そうとします。 。人間の形式でのみユーザーにフィードバックを提供します(ここではツリー出力tree)。


GNU以外のシステムでは、zshシェルを使用して次のことができます。

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

または一度に(一致するファイルが1つ以上あると仮定):

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

u(nique u)交換しましたtypeset -U。パラメータ拡張フラグは、NULで分割するように0 指示する方法です。あるいはzshIFS=$'\0'トークン化を設定して使用することもできます(引用符なしの引数拡張中に実行されます)。

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

GNUユーティリティも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で区切られた文字列に渡されるため)。しかし、パイプを介して供給されるバイトストリームには現れる可能性があるので、単純で明確です。任意のパラメータを分離する方法です。-0GNU実装の非標準的な拡張ですが、他の多くの実装xargsでも見つけることができます。

²または少なくとも1回の通話に入ることができるだけ、dirname必要なだけ通話してください。

答え2

アップデート:新しい最終回答:

sort -zunullで区切られた(-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とwithと一緒に使用することはxargs確かにより強力です。それも使用します--。私はファイルにスペースが含まれていないか、または-ダッシュ()で始まるファイルがないgitリポジトリでこのコマンドを実行しているため、これらのことについてあまり気にしないようです。複数のパスを持つ1回の呼び出しではなく、複数のdirname&呼び出しについてはtreeこれを知っていますが、同意します。部分的には、完全に変更することなく簡単に拡張し、より多くのパイプやコマンドを追加できるという回答が欲しいからです。

それでは、この2つの答えを見てください。彼は技術的に優れていますが、私は現在私の目的に「十分に良い」です。質問の元の例は、最小限の変更でも機能することを指摘しています。前任者:

# 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の作成者が自分で作成したものも含まれます。! )すべてが役に立ち、次のことを見つけるのに役立ちました。これは最も簡単なので、最も簡単で最善の答えだと思います。

の出力パス文字列はrgnullで終わる文字列ではありません。-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 {}

それだけです!私は2つのうちの1つが必要です。次へ追加またはマイナス -0または、両方の呼び出し--nullで一貫性を維持し、複数のパスを確認するときに同じ記述子を期待します。rgxargs

次へ追加 -0ただし、orは--nullパスにスペースやその他のスペースを含めることができるため、より良いです。addもダッシュ()で始まるパスを許可するので、これは良い方法です---だからこれが私が上記でしたことです。

しかし、他の答えも参照してください。また、重複した項目を並べ替え、削除し、その他の複雑な問題を処理することもできます。

また、見ることができます

  1. 私がxargs学んだ内容と例は次のとおりです。
    1. dos2unix複数のプロセスを使用して目的のディレクトリまたはパスで繰り返し実行(または他のコマンド)する方法
    2. xargsここで私の追加情報の例をご覧ください。https://github.com/ElectricRCAircraftGuy/FatFs/tree/main

キーワード:xargsを正しく使用する方法xargsを使用してgrepまたはripgrep rg出力パスを解析します。

関連情報