web-dev-qa-db-ja.com

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

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

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

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

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

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

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

簡略化

最近のsortコマンドの多くは、uniqを不要にする-uフラグをサポートしています。 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/'
45
John1024

これを試してみませんか:

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

これを行うには、基本的に2つの方法があります。 1つは文字列を解析し、もう1つは各ファイルを操作します。 grepsedawkなどのツールを使用して文字列を解析すると、明らかに高速になりますが、両方を示す例と、「プロファイル」 "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-文字列による解析

ここでは、findgrep、およびsortというツールを使用します。

$ 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を使用して、これらの例で処理する出力の量を制限しています。それらは通常、完全なリストを取得するために削除されます。

結果の比較

timeを使用して、2つのアプローチを確認できます。

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
8
slm

これが私が便利だと思うものです:

find . -type f -name "*somefile*" | xargs dirname | sort | uniq
3
Martin Tapp

この回答は恥知らずなことに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

[〜#〜] note [〜#〜]:上記の例では、head -5を使用して、これらの例で処理する出力の量を制限しています。それらは通常、完全なリストを取得するために削除されます。また、echowhichは、使用するコマンドを置き換えます。

1
Franklin Piat

zshの場合:

typeset -aU dirs # array with unique values
dirs=(**/*f*(D:h))

printf '%s\n' $dirs
1