48

拡張機能が何であるかに関係なく、すべての拡張機能を小文字にしようとしています。これまでのところ、私が見てきたことから、小文字に変換するファイル拡張子を指定する必要があります。ただし、名前の最初の最後のドットの後のすべてを小文字にしたいだけです.

どうすればそれを行うことができbashますか?

4

11 に答える 11

80

解決

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"

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

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

; 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

これは、改行を含むファイル名では引き続き機能します。この問題を修正するには、(GNUのように)とBash(区切り文字スイッチをサポートするため)findをサポートするが必要です。-print0findread-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=$(...)サブシェルによって吸収されるためです。本当に,,確実な方法が必要な場合(そしてそうする必要があります!)、パラメーター拡張をサポートする最新バージョンのBash(Bash≥4.0)を使用します。究極の解決策:

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

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

または、一度findに(元のソリューションに戻って、それを本当に確実にするいくつかの修正を加えます):

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見つかったファイルごとにプロセスを生成するのは効率的ではないと主張するかもしれません。それは正しいです、そして以前のループバージョンはそれからより良いでしょう。

于 2012-08-06T08:29:25.000 に答える
60

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

rename JPG jpg *.JPG

ここに、現在のフォルダ内のすべてのファイル名の名前を拡張子が.でrenameあるに変更するようにシェルに指示するコマンドがあります。JPGjpgJPG

このアプローチで(eval 1)行1で「strictsubs」が使用されているときにベアワード「JPG」が許可されていない場合は、次のことを試してください。

rename 's/\.JPG$/.jpg/' *.JPG
于 2014-01-07T04:45:25.417 に答える
14

これはより短いですが、より一般的であり、他の人の答えと組み合わされています。

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
  • この場合の最初のグループは拡張子([^.]+)です
  • シェルの拡張を避けるために、正規表現をラップする'には、二重引用符ではなく一重引用符を使用することをお勧めします"
于 2015-09-23T01:48:45.670 に答える
11

このスニペットは、必要な代替手段のコアとして使用できます。

#!/bin/bash

# lowerext.sh    

while read f; do
    if [[ "$f" = *.* ]]; then
        # Extract the basename
        b="${f%.*}"

        # Extract the extension
        x="${f##*.}"

        # Convert the extension to lower case
        # Note: this only works in recent versions of Bash
        l="${x,,}"

        if [[ "$x" != "$l" ]]; then
            mv "$f" "$b.$l"
        fi
    else
        continue
    fi
done

その後、あなたがする必要があるのは、あなたがその標準的な入力に名前を変更する必要があるファイルのリストを供給することです。たとえば、現在のディレクトリと任意のサブディレクトリの下にあるすべてのファイルの場合:

find -type f | lowerext.sh

小さな最適化:

find -type f -name '*.*' | lowerext.sh

これよりも具体的な答えが必要な場合は、より具体的にする必要があります...

于 2012-08-05T18:09:31.860 に答える
6

ZSHを使用している場合:

zmv '(*).(*)' '$1.$2:l'

取得した場合はzsh: command not found: zmv、単に実行します。

autoload -U zmv

そして、もう一度やり直してください。

zmvに関するヒントについてのこの元の記事と、小文字/大文字の置換構文に関するZSHドキュメントに感謝します。

于 2017-06-20T02:49:58.033 に答える
5

これはあなたの'.mp3のために仕事をします-しかし作業ディレクトリでのみ-しかし空白でファイル名を消費することができます:

for f in *.[mM][pP]3; do mv "$f" "${f%.*}.mp3"; done

修正:

for f in *.[mM][pP]3; do [[ "$f" =~ \.mp3$ ]] || mv "$f" "${f%.*}.mp3"; done
于 2012-08-05T18:22:27.493 に答える
2

すべての1つの優れたソリューションに対して再帰的に:

find -name '*.JPG' | sed 's/\(.*\)\.JPG/mv "\1.JPG" "\1.jpg"/' |sh

上記は、拡張子が「JPG」のファイルの名前を、拡張子が「jpg」のファイルに再帰的に変更します。

于 2015-01-30T10:46:26.597 に答える
1

mmv(=複数のファイルを移動する)がインストールされていて、ファイル名に含まれるドットが最大1つである場合は、次を使用できます。

mmv -v "*.*" "#1.#l2"

右に1つ以上のドットを取得することはありませんが(の一致するアルゴリズム*は貪欲ではないためmmv)、正しく処理さ()'ます。例:

$ mmv -v "*.*" "#1.#l2"
FOO.BAR.MP3 -> FOO.bar.mp3 : done
foo bar 'baz' (CD 1).MP3 -> foo bar 'baz' (CD 1).mp3 : done

完璧ではありませんが、すべてのものよりもはるかに使いやすく、覚えやすいfind/exec/sedです。

于 2013-01-10T15:19:03.140 に答える
0

すべての大文字の「JPG」拡張子を小文字の「jpg」に変換するなど、特定のファイル拡張子にのみ関心がある場合は、コマンドラインユーティリティの名前をそのように変更できます。変更するディレクトリにCDを挿入します。それで

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

-nオプションを使用して何が変更されるかをテストし、結果に満足したら、そのようにせずに使用します

rename  's/\.JPG$/\.jpg/' *
于 2015-08-16T16:47:40.443 に答える
0

私はこれを行うための簡単な方法を(それについて考える必要なしに)探していましたが、最終的にそれを考え抜いて、これを思いつきました(確かに、元の投稿のずっと後)

find . -name \\*.JPG -print -exec rename s/.JPG/.jpg/ {} \\;

私はそれを約60千のファイルで実行し、正常に動作しましたが、もちろん、-n最初にテストしたい場合は、「名前の変更」オプションを使用できます。

于 2019-11-14T17:25:44.063 に答える
-1

したがって、ラインノイズのように見えるこれらのソリューションは素晴らしいものですが、これはpython REPLから簡単に実行できます(OPがbashを要求したことは知っていますが、Pythonは最近bashを使用する多くのシステムにインストールされています... )::

import os
files = os.listdir('.')
for f in files:
    path, ext = os.path.splitext(f)
    if ext.isupper():
        os.rename(f, path + ext.lower())
于 2013-11-10T06:26:54.517 に答える