私はさまざまな歴史的理由で私のシステム全体に散らばっている写真を整理しようとしています。これを開始するために、コマンドラインを使用して1つ以上のjpgファイルを含むすべてのディレクトリのリストを作成しようとしました。他の画像ファイル形式を見つけることを心配する必要はありませんが、jpgが大文字と小文字で表示されることを許可する必要があります。
各ディレクトリ名が最終リストに一度だけ現れることを望みます。たとえば、次のディレクトリがある場合、各ディレクトリには1つ以上のjpgまたはJPGファイルが含まれます。
~Mike/Pictures
~Mike/Pictures/London/Olympics
~Mike/Pictures/London
~Mike/Pictures/London/Holiday
~Mike/Photos
~Mike/Family History/Swaine
結果がディレクトリごとに一度だけ一覧表示されます。含まれる画像ファイルの数に関係なく、最初に並べ替えてからファイルに書き込むことをお勧めします。
~Mike/Family History/Swaine
~Mike/Photos
~Mike/Pictures
~Mike/Pictures/London
~Mike/Pictures/London/Holiday
~Mike/Pictures/London/Olympics
私のコマンドライン技術はまだこのレベルに達していません!いくつかの単純な形式の個々のコマンドを使用できますが、一度複雑になったりパイプを接続したりすると、問題が発生する傾向があります。
答え1
JPEG画像ファイルのサフィックスは、.jpg
または次のように見なされます.JPG
。
find "$HOME" -type f \( -name '*.jpg' -o -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' -o -name '*.JPG' \) -printf '%h\n' | sort -u -o jpeg_dirs.txt
このfind
コマンドは、ホームディレクトリ内のすべてのJPEGイメージを検索し、そのイメージがあるディレクトリの名前を印刷します。このディレクトリ名のリストを取得してsort -u
並べ替え、重複エントリを削除します。結果はjpeg_dirs.txt
現在のディレクトリのファイルに書き込まれます。
2021年初め(3.3年後)にこれを振り返ってみると、上記の解決策はそれ自体は間違っているわけではありませんが、少し逆さまになったので少し煩わされます。また、「良いファイル名」(改行なし)について明らかな仮定をします。
find
ディレクトリを検索するときは、上記のように通常のファイルを検索しないでください。ディレクトリが存在する場合は、各ディレクトリを調べて、一致するファイルがあるかどうか、または*.jpg
他*.JPG
のファイル名のサフィックスが簡単に追加されることを確認できます。
find "$HOME" -type d -exec bash -O nullglob -O dotglob -O extglob -c '
for dirpath do
set -- "$dirpath"/*.@(jpg|JPG)
[ "$#" -eq 0 ] || printf "%s\n" "$dirpath"
done' bash {} +
これはホームディレクトリの下に各ディレクトリを見て、*.@(jpg|JPG)
各ディレクトリのワイルドカードパターンを拡張しようとします。このパターンは2つの別々のパターンで作成することも、*.jpg
私*.JPG
たちが探しているすべてのファイルと一致させることもできます。名前が一致した場合、これは出力したい名前のディレクトリであると仮定します。これは、以下を含むディレクトリに対して誤検出を提供します。サブディレクトリこの接尾辞として。
内部スクリプトを実行するために私たちが持っているシェルオプションを使用すると、隠された名前をbash
一致させることができ(dotglob
)、拡張されていないままの代わりに何も一致しないときにglobbingパターンが完全に消えることを許可し(nullglob
)、ksh
-inspiredを使用してglobbingパターンを拡張できます@(...|...)
。
zsh
シェルの使用:
typeset -U list=(~/**/*.(jpg|JPG)(.DN:h))
print -rC1 $list
list
これにより、一意の要素のみを格納する属性を持つ配列変数が作成されます。拡張ファイル名はワイルドカードパターンの結果として初期化されます。このパターンはホームディレクトリまたはその下のすべてのJPEG画像ファイルと一致し、:h
最後のパターンは生成されたパス名から実際のファイル名を削除します。.
パターンが通常のファイルにのみ一致するようにしますD
。N
dotglob
nullglob
bash
答え2
これを行う簡単な方法は、すべてのファイルを一覧.jpg
表示し、ファイルのデフォルト名(最後のスラッシュの次の部分)を削除して重複エントリを削除することです。を使用すると、sed
最後のスラッシュの後の各行部分を削除できます。という重複エントリを削除するコマンドがありますが、uniq
入力がソートされているとします。とにかくソートが必要な場合はsort
一意にすることができます。
find ~Mike -iname '*.jpg' | sed 's!/[^/]*$!!' | sort -u >directories_with_jpeg_files.txt
これは、関連するディレクトリまたはファイル名に改行文字がないと仮定します。改行を含むファイル名は通常の状況では表示されませんが、ファイル名が攻撃者によって選択される可能性があることに注意してください(たとえば、サーバーにアップロードされたファイルを処理していて、アップローダーがファイル名を選択できる場合)。
JPEGファイルがたくさんあるディレクトリがあり、そうでないディレクトリがたくさんない場合、この方法を使用すると重複ファイルを報告するのに時間がかかることがあります。 findがディレクトリに何かを見つけたら、ショートカットを知らせる方法はありません。ただし、検索をディレクトリに制限し、各ディレクトリにJPEGファイルを検索するように指示できます。ただし、これによりJPEGファイルが含まれていないディレクトリのコストが増加するため、JPEGlessディレクトリが多いとパフォーマンスが低下する可能性があります。
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
せることもできます。ディレクトリ部分を抽出するには、glob修飾子に履歴修飾子を追加します。これを配列変数に埋め込み、パラメータ拡張フラグを使用して配列の一意の要素を抽出します。*.jpg
*.JPG
.Jpg
h
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
glob修飾子を使用することです。
print -lr ~Mike/**/*(/e\''set -- $REPLY/*.(#i)jpg(N[1]); (($# != 0))'\') >directories_with_jpeg_files.txt
答え3
find . -iname '*.jpg' -execdir sh -c 'pwd' _ {} + | sort -u > dirs_with_jpegs.txt
find
サポートを実装すると仮定すると、正常に動作します-execdir
(おそらくはい)。-execdir
見つかったファイルがあるディレクトリからコマンドを実行します。この例では、pwd
ディレクトリ名を印刷するコマンドを実行します。sh -c
コマンドをストリップパラメータでラップします。 (一部(すべて?)実装では、現在のディレクトリにあるjpegファイルのリストになるパラメータ置換がfind
必要です。リストを無視してディレクトリのみを印刷しようとしています。){}