GNU/Linux >> Linux の 問題 >  >> Linux

すべてのファイル拡張子を小文字に変換する

このコマンドで成功しました。

rename JPG jpg *.JPG

どこで rename JPG が出現するたびに名前を変更するようシェルに指示するコマンドです。 jpgまで すべてのファイル名の拡張子が JPG の現在のフォルダー .

このアプローチで (eval 1) 行 1 で "strict subs" が使用されているときに Bareword "JPG" not allowed が表示された場合は、次を試してください:

rename 's/\.JPG$/.jpg/' *.JPG

解決策

タスクは 1 行で解決できます:

find . -name '*.*' -exec sh -c '
  a=$(echo "$0" | sed -r "s/([^.]*)\$/\L\1/");
  [ "$a" != "$0" ] && mv "$0" "$a" ' {} \;

注:これは、改行を含むファイル名では壊れます。しかし、今は我慢してください。

使用例

$ mkdir C; touch 1.TXT a.TXT B.TXT C/D.TXT
$ find .
.
./C
./C/D.TXT
./1.TXT
./a.TXT
./B.TXT

$ find . -name '*.*' -exec sh -c 'a=$(echo "$0" | sed -r "s/([^.]*)\$/\L\1/"); [ "$a" != "$0" ] && mv "$0" "$a" ' {} \;

$ find .
.
./C
./C/D.txt
./a.txt
./B.txt
./1.txt

説明

すべてのファイルが現在のディレクトリ (.) にあります。 ) ピリオド . を持つもの その名前 (-name '*.*' )、各ファイルに対してコマンドを実行します:

a=$(echo "$0" | sed -r "s/([^.]*)\$/\L\1/");
[ "$a" != "$0" ] && mv "{}" "$a"

このコマンドの意味:ファイル拡張子を小文字に変換しようとします (つまり sed になります) ):

$ echo 1.txt | sed -r "s/([^.]*)\$/\L\1/"
1.txt
$ echo 2.TXT | sed -r "s/([^.]*)\$/\L\1/"
2.txt

結果を a に保存します

何かが変更された場合 [ "$a" != "$0" ] 、ファイルの名前を mv "$0" "$a" に変更します .

処理中のファイルの名前 ({} ) sh -c に渡される 追加の引数として、コマンド ライン内で $0 として表示されます。 .これにより、スクリプトが安全になります。この場合、コマンド ラインで直接指定されたときのように、シェルは {} をコード部分としてではなくデータとして受け取るためです。(ありがとうございます @gniourf_gniourf この本当に重要な問題を教えてくれて ).

ご覧のとおり、 {} を使用すると スクリプトに直接、次のようなファイル名にいくつかのシェルインジェクションを含めることができます:

; rm -rf * ;

この場合、インジェクションはシェルによってコードの一部と見なされ、実行されます。

While バージョン

スクリプトのより明確ですが、少し長いバージョン:

find . -name '*.*' | while IFS= read -r f
do
  a=$(echo "$f" | sed -r "s/([^.]*)\$/\L\1/");
  [ "$a" != "$f" ] && mv "$f" "$a"
done

これは、改行を含むファイル名の場合でも壊れます。この問題を解決するには、find が必要です -print0 をサポートする (GNU find のように ) および Bash (そのため read -d をサポート 区切りスイッチ):

find . -name '*.*' -print0 | while IFS= read -r -d '' f
do
  a=$(echo "$f" | sed -r "s/([^.]*)\$/\L\1/");
  [ "$a" != "$f" ] && mv "$f" "$a"
done

これは、末尾の改行を含むファイルではまだ壊れています (a=$(...) によって吸収されるため)。 サブシェル。あなたが本当に ,,. パラメータ拡張は究極のソリューションです:

find . -name '*.*' -print0 | while IFS= read -r -d '' f
do
  base=${f%.*}
  ext=${f##*.}
  a=$base.${ext,,}
  [ "$a" != "$f" ] && mv -- "$f" "$a"
done

元のソリューションに戻る

または 1 つの find で go (元のソリューションに戻り、いくつかの修正を加えて、本当に簡単にできるようにします):

find . -name '*.*' -type f -exec bash -c 'base=${0%.*} ext=${0##*.} a=$base.${ext,,}; [ "$a" != "$0" ] && mv -- "$0" "$a"' {} \;

-type f を追加しました 通常のファイルのみが名前変更されるようにします。これがないと、ファイル名の前にディレクトリ名が変更された場合に問題が発生する可能性があります。ディレクトリ (およびリンク、パイプなど) の名前も変更する場合は、-depth を使用する必要があります。 :

find . -depth -name '*.*' -type f -exec bash -c 'base=${0%.*} ext=${0##*.} a=$base.${ext,,}; [ "$a" != "$0" ] && mv -- "$0" "$a"' {} \;

だから find 深さ優先検索を実行します。

bash を生成するのは効率的ではないと主張するかもしれません 見つかった各ファイルのプロセス。その通りです。以前のバージョンのループの方が優れています。


これはより短いですが、より一般的であり、他の回答から結合されています:

rename 's/\.([^.]+)$/.\L$1/' *

シミュレーション

シミュレーションには -n を使用します 、つまり rename -n 's/\.([^.]+)$/.\L$1/' * .このようにして、実際の変更が実行される前に何が変更されるかを確認できます。出力例:

Happy.Family.GATHERING.JPG renamed as Happy.Family.GATHERING.jpg
Hero_from_The_Land_Across_the_River.JPG renamed as Hero_from_The_Land_Across_the_River.jpg
rAnD0m.jPg1 renamed as rAnD0m.jpg1

構文についての簡単な説明

  • 構文は rename OPTIONS 's/WHAT_TO_FIND_IN_THE_NAME/THE_REPLACEMENT/' FILENAMES です
  • \.([^.]+)$ ドット以外の連続を意味します ([^.] ) 文字列の末尾 ($ )、ドットの後 (\. )
  • .\L$1 ドットを意味します (\. ) の後に小文字 (\L) が続く ) の 1 グループ ($1) )
  • この場合の最初のグループは拡張子 ([^.]+) です )
  • 一重引用符 ' を使用することをお勧めします 二重引用符 " の代わりに 正規表現をラップしてシェルの拡張を避ける

Linux
  1. 1つのファイルを除くすべてのファイル/ディレクトリを削除しますか?

  2. ファイル拡張子をクリーンアップする方法は?

  3. すべてのTmuxスクロールバックをファイルに書き込みますか?

  1. 一致するまですべてのファイルを表示しますか?

  2. すべての端末出力をファイルに保存しますか?

  3. フォルダー階層内の個別のファイル拡張子をすべて見つけるにはどうすればよいですか?

  1. シンボリック リンク:このファイルにリンクしているすべてのファイルを検索します

  2. ISO8859-15をUTF8に変換するには?

  3. Linux で Bash を使用してすべての出力をファイルにリダイレクトしますか?