web-dev-qa-db-ja.com

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

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

bashでどうすればよいですか?

37
thevoipman

ソリューション

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-version

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

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

改行を含むファイル名の場合、これはまだ壊れています。この問題を修正するには、-print0_(GNU find)など)とBash(findをサポート)をサポートする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=$(...)サブシェルに吸収されるためです。本当に絶対確実な方法が必要な場合(そしてそうすべきです! )、,,_パラメーター展開をサポートする最新バージョンの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プロセスを生成するのは効率的ではないと主張するかもしれません。それは正しいです、そして、以前のループバージョンはより良いでしょう。

65
Igor Chubin

これは短くなりますが、より一般的であり、他の回答から組み合わされます:

_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_は、ドット(_\._)の後に1の小文字(_\L_)が続くことを意味しますst グループ(_$1_)
  • この場合の最初のグループは拡張子(_[^.]+_)です
  • 二重引用符_'_の代わりに単一引用符_"_を使用して、シェル拡張を回避するために正規表現をラップすることをお勧めします
12
fikr4n

さて、このスニペットを必要な代替のコアとして使用できます:

#!/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

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

10
thkala

これにより、「。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
3

ZSHを使用している場合:

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

あなたが取得する場合 zsh: command not found: zmvその後、単に実行します。

autoload -U zmv

そして、もう一度試してください。

これに感謝 元の記事 zmvに関するヒントと ZSHドキュメンテーション 小文字/大文字の置換構文。

3
Michael Leonard

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

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

複数のドットを取得することはありません(*の一致するアルゴリズムは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のすべてのものよりも使いやすく覚えやすいです。

1
Elmar Zander

すべての素晴らしい解決策を再帰的に:

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

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

1
Govind Totla

JDKがインストールされている場合は、次のように単純にコンパイルできます。

import Java.io.File;

public class FileRename {
    public static void main(String[] args) {
        long numFilesChanged = 0;
        long numFilesNotChanged = 0;

        try {
            final File directory = new File(".");
            final File[] listOfFiles = directory.listFiles();

            for (int i = 0; i < listOfFiles.length; i++) {
                if (!listOfFiles[i].isFile()) {
                    continue;
                }

                final File fileToRename = listOfFiles[i];

                final String filename = fileToRename.getName();
                final String woExtension = filename.substring(0, filename.lastIndexOf('.'));
                final String extension = filename.substring(filename.lastIndexOf('.'));

                if (extension.toLowerCase().equals(extension)) {
                    numFilesNotChanged++;
                } else {
                    numFilesChanged++;
                    fileToRename.renameTo(new File(woExtension + extension.toLowerCase()));
                }

            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        System.out.println("Changed: " + numFilesChanged);
        System.out.println("Not changed:" + numFilesNotChanged);
    }
}

実行します。

JDKがインストールされていないがJavaがインストールされている場合、オンラインのJavaコンパイラを使用してコンパイルします。 https://www.compilejava .net / そして、結果の.classファイルをファイルの名前を変更して実行するフォルダーに配置します:Java FileRename

Javaに精通していない場合や、コンパイルや実行にヘルプが必要な場合は、お気軽にご連絡ください。

0
Koray Tugay

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

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

-nオプションを使用して、何が変更されるかをテストし、結果に満足したら、そのように使用しないでください

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