名前が特定のパターンに一致するファイルを含むサブディレクトリのリストを取得するにはどうすればよいですか?
より具体的には、ファイル名のどこかに 'f'という文字が含まれているファイルを含むディレクトリを探しています。
理想的には、リストには重複がなく、ファイル名なしのパスのみが含まれます。
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
上記のバージョンは、改行を含むファイル名と混同されます。より堅牢なソリューションは、NULで終わる文字列でソートを行うことです。
find . -type f -name '*f*' -printf '%h\0' | sort -zu | sed -z 's/$/\n/'
これを試してみませんか:
find / -name '*f*' -printf "%h\n" | sort -u
これを行うには、基本的に2つの方法があります。 1つは文字列を解析し、もう1つは各ファイルを操作します。 grep
、sed
、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*
ここでは、find
、grep
、およびsort
というツールを使用します。
$ find . -type f -name '*f*' | grep -o "\(.*\)/" | sort -u | head -5
./dir1/dir103/
./dir1/dir104/
./dir1/dir105/
./dir1/dir106/
./dir1/dir107/
以前と同じツールチェーンですが、今回は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
これが私が便利だと思うものです:
find . -type f -name "*somefile*" | xargs dirname | sort | uniq
この回答は恥知らずなことに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*
$ 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
を使用して、これらの例で処理する出力の量を制限しています。それらは通常、完全なリストを取得するために削除されます。また、echo
whichは、使用するコマンドを置き換えます。
zsh
の場合:
typeset -aU dirs # array with unique values
dirs=(**/*f*(D:h))
printf '%s\n' $dirs