web-dev-qa-db-ja.com

1つ以上のjpg画像ファイルを含むすべてのディレクトリを再帰的に一覧表示します

さまざまな歴史的な理由から、システム全体に散らばっている写真を整理しようとしています。このタスクを開始できるようにするために、コマンドラインを使用して、1つ以上のjpgファイルを含むすべてのディレクトリのリストを作成しようとしています。他の画像ファイル形式を探す必要はないと確信していますが、jpgが大文字と小文字で表示されるようにする必要があります。

各ディレクトリ名を最終リストに1回だけ表示したいと思います。例を挙げると、次のディレクトリがあり、それぞれに1つ以上のjpgまたはJPGファイルが含まれている場合...

~Mike/Pictures
~Mike/Pictures/London/Olympics
~Mike/Pictures/London
~Mike/Pictures/London/Holiday
~Mike/Photos
~Mike/Family History/Swaine

含まれている可能性のある画像ファイルの数に関係なく、各ディレクトリが1回だけリストされた状態で結果を表示したいのですが、ソートしてからファイルに書き込むことが望ましい

~Mike/Family History/Swaine
~Mike/Photos
~Mike/Pictures
~Mike/Pictures/London
~Mike/Pictures/London/Holiday
~Mike/Pictures/London/Olympics

私のコマンドラインスキルはこれまでではありません!単一のコマンドのより単純な形式をたくさん使用できますが、それらが複雑になったり、パイプ処理する必要があると、問題が発生する傾向があります。

2
Midahed

JPEG画像ファイルの接尾辞が.jpgであると仮定します。

find "$HOME" -type f -name '*.jpg' \
    -exec sh -c 'for d; do dirname "$d"; done' sh {} + | sort -u -o jpeg_dirs.txt

これは、名前に改行が含まれるファンキーなディレクトリ名がないことに依存しています。

GNU find

find "$HOME" -type f -name '*.jpg' -printf '%h\n' | sort -u -o jpeg_dirs.txt

これらのfindコマンドは、ホームディレクトリの下にあるすべてのJPEG画像を検索し、それらが見つかったディレクトリの名前を出力します。 sort -uは、このディレクトリ名のリストを取得して並べ替え、重複を削除します。結果は、現在のディレクトリのファイルjpeg_dirs.txtに書き込まれます。

3
Kusalananda

簡単な方法は、すべての.jpgファイルを一覧表示してから、ファイルのベース名(最後のスラッシュの後の部分)を取り除き、重複を削除することです。 sedを使用して、最後のスラッシュの後に各行の一部を削除できます。重複を削除するコマンドがあります。これはuniqと呼ばれますが、ソートされた入力を想定しています。とにかくソートする必要がある場合は、sortに一意化を行わせることができます。

find ~Mike -iname '*.jpg' | sed 's!/[^/]*$!!' | sort -u >directories_with_jpeg_files.txt

これは、関係するディレクトリまたはファイルのいずれにも名前に改行が含まれていないことを前提としています。新しい行のあるファイル名は通常の状況では表示されませんが、ファイル名が敵対者によって選択された可能性がある場合は注意してください(たとえば、サーバーにアップロードされたファイルを処理していて、アップローダーがファイル名を選択できる場合)。 。

JPEGファイルを多く含むディレクトリがあり、JPEGファイルを含まないディレクトリが少ない場合、この方法では、冗長ファイルのレポートに多くの時間がかかります。ディレクトリ内で何かが見つかったら、ディレクトリをショートカットするようにfindに指示する方法はありません。ただし、検索をディレクトリに制限して、各ディレクトリでJPEGファイルを検索するように指示することはできます。これにより、JPEGファイルを含まないディレクトリのコストが増加するため、JPEGなしのディレクトリが多数ある場合はパフォーマンスが低下する可能性があります。

find ~Mike -type d -exec sh -c '
    for d do
      set -- "$d/*.[Jj][Pp][Gg]";
      if [ -e "$1" ]; then printf %s\\n "$d"; fi
    done
' sh {} + | sort -u >directories_with_jpeg_files.txt

または、zshでは、**ワイルドカードを使用してディレクトリを再帰的にトラバースし、(#i)を使用して、大文字と小文字を区別せずに次のパスコンポーネントに一致させ、パターン**/(#i)*.jpg*.jpg*.JPG(および.Jpgなど)に全体的に一致させることができます。ディレクトリツリー。履歴修飾子hをglob修飾子に追加して、ディレクトリ部分を抽出します。これを配列変数dirs=(…)に詰め込み、uパラメーター展開フラグを使用してこの配列の一意の要素を抽出します。

set -o extendedglob # for (#i); best in ~/.zshrc
dirs=(~Mike/**/(#i)*.jpg(:h))
print -lr -- ${(u)dirs} >directories_with_jpeg_files.txt

上記のディレクトリごとのチェック方法と同等の方法は、eグロブ修飾子を使用することです。

print -lr ~Mike/**/*(/e\''set -- $REPLY/*.(#i)jpg(N[1]); (($# != 0))'\') >directories_with_jpeg_files.txt
find . -iname '*.jpg' -execdir sh -c 'pwd' _ {} + | sort -u > dirs_with_jpegs.txt

findの実装が-execdirをサポートしていると仮定すると、十分に機能するのは難しいでしょう(おそらくサポートしています)。 -execdirは、見つかったファイルがあるディレクトリでコマンドを実行します。この場合、コマンドpwdを実行して、ディレクトリの名前を出力します。コマンドをsh -cでラップして、引数を削除します。 (findの一部(すべて?)の実装では、{}引数の置換が必要です。これは、現在のディレクトリ内のjpegファイルのリストです。このリストを無視して、ディレクトリのみを出力します。)

0
kojiro