たとえば、20200403のような日付で始まる多くのファイルを含むフォルダがあります。同じ日付の複数のファイルを含むフォルダを探したいです。つまり、最初の8文字が同じです。日付は各特定のフォルダ内でのみ重要です。フォルダ間では重要ではありません。
ファイルメタデータがファイル名の日付と必ずしも一致する必要はないため、それを見つける方法としては使用できません。
答え1
適切にソートされたファイル名のリストを渡すことを検討してくださいuniq -d
。uniq
たとえば、シェルが照合順序に対して同じアイデアを持っているとします。
printf -- "%s\n" * | cut -c1-8 | uniq -d
結果が空でない場合は、重複項目が必要です。find
次のコマンドで包みます。
find . -type d -exec sh -c '
cd "$1" && test -n "$(printf -- "%s\n" * | cut -c1-8 | uniq -d)"
' find-sh {} \; -print
だから与えられた
$ tree .
.
├── subdir1
│ └── 20200403foo
├── subdir2
│ ├── 20200403bar
│ └── 20200403foo
├── subdir3
│ └── 20200403foo
├── subdir4
│ ├── 20200403bar
│ └── 20200403foo
└── subdir5
└── 20200403foo
5 directories, 7 files
それから
$ find . -type d -exec sh -c 'cd "$1" && test -n "$(printf -- "%s\n" * | cut -c1-8 | uniq -d)"' find-sh {} \; -print
./subdir4
./subdir2
改行を含めて空の区切り文字をサポートするファイル名を処理する必要がある場合は、パイプを次のように変更できますcut
。uniq
printf "./%s\0" * | cut -zc1-10 | uniq -zd
答え2
改行文字を含む任意のファイルパスを正しく処理しますが、ある程度エレガントではなく遅い移植可能なソリューションです。
find /path/to/dir -type d \( -exec sh -c '
cd "$1"
printf "%s/" [0123456789][0123456789][0123456789][0123456789][01][0123456789][0123][0123456789]* \
| awk -v RS="/" "seen[substr(\$0,1,8)]++ { exit 1 }"
' mysh {} \; -o -print \)
find
ディレクトリを再帰的に検索し、/path/to/dir
ほぼ日付に似たパターン(yyyy / mm / dd形式と仮定)に一致するファイル名をパイプする検索された各ディレクトリでスクリプトを実行するために使用されます。/
で終わる区切りレコードを読み取る各スクリプトインスタンスは状態で終了します。awk
inputで繰り返される8文字(最初の文字から始まる)文字列が見つかるとすぐに、/
ディレクトリ名が編集されます。1
-print
GNUツールに基づくより速い選択肢:
find /path/to/dir -type f -name '[0123456789][0123456789][0123456789][0123456789][01][0123456789][0123][0123456789]*' \
-print0 | awk -v FS='/' -v OFS='/' -v RS='\0' '
{ file=substr($NF,1,8); $NF=""; dir=$0 }
seen[dir file]++ { dupl[dir] }
END { for (d in dupl) print d }'
ここでは、名前が(ほぼ)日付で始まる通常のファイルのみにパイプされます/path/to/dir
。見つかったファイルパスは、awk
NULで区切られたレコードストリームにパイプされます。各レコードの最後のコンポーネント(ファイル名)の最初の8文字だけが保持され、結果のパスは連想配列に格納されます。重複エントリが見つかると、ディレクトリ部分(つまり、ファイル名部分が削除されたパス)が印刷されます。