名前に文字列が含まれるファイルを含むサブディレクトリのリストを取得します


45

特定のパターンに一致する名前のファイルを含むサブディレクトリのリストを取得するにはどうすればよいですか?

より具体的には、ファイル名のどこかに文字「f」が含まれるファイルを含むディレクトリを探しています。

理想的には、リストには重複がなく、ファイル名なしのパスのみが含まれることになります。

回答:


43
find . -type f -name '*f*' | sed -r 's|/[^/]+$||' |sort |uniq

上記は、現在のディレクトリ(.)の下にある通常のファイル(-type f)でありf、名前のどこかにあるすべてのファイル()を検索します-name '*f*'。次に、sedファイル名を削除し、ディレクトリ名のみを残します。次に、ディレクトリのリストがソートされ(sort)、重複が削除されます(uniq)。

sedコマンドは、1つの代替で構成されています。正規表現への一致を探し、一致する/[^/]+$ものを何も置き換えません。ドル記号は行の終わりを意味します。 [^/]+'スラッシュではない1つ以上の文字を意味します。したがって、/[^/]+$最後のスラッシュから行末までのすべての文字を意味します。つまり、これはフルパスの末尾のファイル名と一致します。したがって、sedコマンドはファイル名を削除し、ファイルがあったディレクトリの名前を変更せずに残します。

簡素化

最新のsortコマンドの多く-uは、uniq不要なフラグをサポートしています。GNU sedの場合:

find . -type f -name '*f*' | sed -r 's|/[^/]+$||' |sort -u

そして、MacOS sedの場合:

find . -type f -name '*f*' | sed -E 's|/[^/]+$||' |sort -u

また、findコマンドでサポートされている場合はfind、ディレクトリ名を直接印刷することもできます。これにより、以下の必要がなくなりますsed

find . -type f -name '*f*' -printf '%h\n' | sort -u

より堅牢なバージョン(GNUツールが必要)

上記のバージョンは、改行を含むファイル名と混同されます。より堅牢なソリューションは、NULで終了する文字列でソートを行うことです。

find . -type f -name '*f*' -printf '%h\0' | sort -zu | sed -z 's/$/\n/'

並べ替えにコストがかかりすぎるファイルがたくさんあります。投げるuniqミックスにしても、すでにお互いに隣している重複行を削除することで多くのことができます。 find . -type f -name '*f*' -printf '%h\0' | uniq -z | sort -zu | tr '\0' '\n'。または、ツールが少し古い場合、uniqに-zオプションがない場合があります。find . -type f -name '*f*' -printf '%h\n' | uniq | sort -u
jbo5112

1
MacOSユーザー:sedフラグは-rではありません。何らかの理由で
デビッド

@David非常に本当です。回答-EがMacOS向けに更新されました。
John1024

23

これを試してみてください:

find / -name '*f*' -printf "%h\n" | sort -u

ベストアンサー。上記のいくつかの回答とは異なり、POSIXに完全に対応しており、特別なThe Shortest Pipeline賞も獲得できます:)。
kkm

誰かがこのタイミングと上記の他のタイミングを見せてくれるのを楽しみにしています。
dlamblin

4
@kkmこれが最善の解決策であることに同意しますが、POSIXの仕様findは実際には非常にまばらであり、-printf演算子は指定されていません。これはBSDでは機能しませんfind。したがって、「完全にPOSIX互換」ではありません。(ただしsort -u POSIXにあります。)
ワイルドカード

8

これを行うには、基本的に2つの方法があります。1つは文字列を解析し、もう1つは各ファイルを操作します。以下のようなツールの文字列の使用を解析するgrepsedまたはawk明らかに速いことになるだろうが、ここでは両方を示す一例だ、だけでなく、どのようにすることができます「プロファイル」の2つの方法です。

サンプルデータ

以下の例では、次のデータを使用します

$ touch dir{1..3}/dir{100..112}/file{1..5}
$ touch dir{1..3}/dir{100..112}/nile{1..5}
$ touch dir{1..3}/dir{100..112}/knife{1..5}

*f*からいくつかのファイルを削除しますdir1/*

$ rm dir1/dir10{0..2}/*f*

アプローチ#1-文字列を介した解析

ここでは、以下のツールを使用するつもりだfindgrepsort

$ find . -type f -name '*f*' | grep -o "\(.*\)/" | sort -u | head -5
./dir1/dir103/
./dir1/dir104/
./dir1/dir105/
./dir1/dir106/
./dir1/dir107/

アプローチ#2-ファイルを使用した解析

以前と同じツールチェーン。ただし、今回はのdirname代わりに使用しますgrep

$ find . -type f -name '*f*' -exec dirname {} \; | sort -u | head -5
./dir1/dir103
./dir1/dir104
./dir1/dir105
./dir1/dir106
./dir1/dir107

注:上記の例はhead -5、これらの例で扱っている出力の量を単に制限するために使用しています。通常は、完全なリストを取得するために削除されます!

結果の比較

我々は使用することができますtime2つのアプローチを見てみましょうします。

dirname

real        0m0.372s
user        0m0.028s
sys         0m0.106s

grep

real        0m0.012s
user        0m0.009s
sys         0m0.007s

そのため、可能な場合は常に文字列を処理するのが最善です。

代替の文字列解析方法

grepおよびPCRE

$ find . -type f -name '*f*' | grep  -oP '^.*(?=/)' | sort -u

sed

$ find . -type f -name '*f*' | sed 's#/[^/]*$##' | sort -u

awk

$ find . -type f -name '*f*' | awk -F'/[^/]*$' '{print $1}' | sort -u

1、それは動作しますが、興味深いことに、これはJohn1024の答え@より長い何回もかかるので
Muhd

@Muhd-はい、dirnameの呼び出しは遅いです。私は代替案に取り組んでいます。
slm


1

この答えは、slmの答えに恥知らずに基づいています。興味深いアプローチでしたが、ファイル名やディレクトリ名に特別な文字(スペース、半列など)が含まれている場合には制限があります。良い習慣は使用することfind /somewhere -print0 | xargs -0 someprogamです。

サンプルデータ

以下の例では、次のデータを使用します

mkdir -p dir{1..3}/dir\ {100..112}
touch dir{1..3}/dir\ {100..112}/nile{1..5}
touch dir{1..3}/dir\ {100..112}/file{1..5}
touch dir{1..3}/dir\ {100..112}/kni\ fe{1..5}

*f*からいくつかのファイルを削除しますdir1/*/

rm dir1/dir\ 10{0..2}/*f*

アプローチ#1-ファイルを使用した解析

$ find -type f -name '*f*' -print0 | sed -e 's#/[^/]*\x00#\x00#g' | sort -zu | xargs -0 -n1 echo | head -n5
./dir1/dir 103
./dir1/dir 104
./dir1/dir 105
./dir1/dir 106
./dir1/dir 107

:上記の例はhead -5、これらの例で扱っている出力の量を単に制限するために使用しています。通常は、完全なリストを取得するために削除されます!また、echo使用するコマンドをwhich に置き換えます。


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.