私は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
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回表示されます。tree
dir
dir/subdir
dir/subdir
あなたの考えは正しいです。xargs
which isコマンドを使用してバイト文字列をパラメータリストに変換してコマンドに渡し、-0
whichを使用すると、任意のパラメータリストを渡す最も信頼性の高い方法になります。
xargs -0
入力は、パラメーター・リストがNUL文字(0バイト)で区切られた形式であると予想されます。この形式でファイルリストを印刷するには、-0
/オプションが必要です。--null
rg
- GNUは
dirname
呼び出しごとに複数の引数を処理できるため、-I{}
これを使用する代わりにすべての引数を渡します。また、ファイルのリストが空の場合、それをまったく呼び出さずに-r
NULで区切られたディレクトリを印刷するオプション自体もGNUに対応します。dirname
-z
dirname
dirname
- 各ファイルにはプレフィックスが付いていないため、ファイル名の前に 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
指示する方法です。あるいはzsh
、IFS=$'\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で区切られた文字列に渡されるため)。しかし、パイプを介して供給されるバイトストリームには現れる可能性があるので、単純で明確です。任意のパラメータを分離する方法です。-0
GNU実装の非標準的な拡張ですが、他の多くの実装xargs
でも見つけることができます。
²または少なくとも1回の通話に入ることができるだけ、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
と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の作成者が自分で作成したものも含まれます。! )すべてが役に立ち、次のことを見つけるのに役立ちました。これは最も簡単なので、最も簡単で最善の答えだと思います。
の出力パス文字列はrg
nullで終わる文字列ではありません。-0
xargs
コマンドから削除(または逆に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で終了させることもできます。--null
rg
# 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
で一貫性を維持し、複数のパスを確認するときに同じ記述子を期待します。rg
xargs
次へ追加 -0
ただし、orは--null
パスにスペースやその他のスペースを含めることができるため、より良いです。addもダッシュ()で始まるパスを許可するので、これは良い方法です--
。-
だからこれが私が上記でしたことです。
しかし、他の答えも参照してください。また、重複した項目を並べ替え、削除し、その他の複雑な問題を処理することもできます。
また、見ることができます
- 私が
xargs
学んだ内容と例は次のとおりです。
キーワード:xargsを正しく使用する方法xargsを使用してgrepまたはripgrep rg出力パスを解析します。