web-dev-qa-db-ja.com

Bashを使用したファイル名の一括変更

一連のパッケージの名前を変更してバージョン番号を削除するにはどうすればよいですか?私はexpr%%の両方をいじっていましたが、何の役にも立ちませんでした。

例:

Xft2-2.1.13.pkgXft2.pkgになります

jasper-1.900.1.pkgjasper.pkgになります

xorg-libXrandr-1.2.3.pkgxorg-libXrandr.pkgになります

93
Jeremy L

Bashのパラメーター拡張機能を使用できます

for i in ./*.pkg ; do mv "$i" "${i/-[0-9.]*.pkg/.pkg}" ; done

スペースを含むファイル名には引用符が必要です。

177
richq

すべてのファイルが同じディレクトリにある場合、シーケンス

ls | 
sed -n 's/\(.*\)\(-[0-9.]*\.pkg\)/mv "\1\2" "\1.pkg"/p' | 
sh

あなたの仕事をします。 sedコマンドは一連のmvコマンドを作成し、シェルにパイプすることができます。最初に、末尾の| shなしでパイプラインを実行して、コマンドが目的どおりに動作することを確認することをお勧めします。

複数のディレクトリを再帰するには、次のようなものを使用します

find . -type f |
sed -n 's/\(.*\)\(-[0-9.]*\.pkg\)/mv "\1\2" "\1.pkg"/p' |
sh

sedでは、正規表現のグループ化シーケンスは、単一のブラケット\(および\)ではなく、バックスラッシュ(および)が前に付いたブラケットです。 。

32

私はこのようなことをします:

for file in *.pkg ; do
    mv $file $(echo $file | rev | cut -f2- -d- | rev).pkg
done

すべてのファイルが現在のディレクトリにあると仮定します。そうでない場合は、上記のJavierのアドバイスに従ってfindを使用してみてください。

[〜#〜] edit [〜#〜]:また、このバージョンは上記の他のようなbash固有の機能を使用しないため、移植性が向上します。

10
Diego Sevilla

現在受け入れられている答え とほぼ同等のPOSIXを次に示します。これにより、Bash専用の${variable/substring/replacement}パラメーター拡張が、Bourne互換シェルで使用可能なパラメーター拡張と引き換えになります。

for i in ./*.pkg; do
    mv "$i" "${i%-[0-9.]*.pkg}.pkg"
done

パラメータ拡張${variable%pattern}は、variableに一致する任意の接尾辞を削除したpatternの値を生成します。 (プレフィックスを削除する${variable#pattern}もあります。)

おそらく誤解を招くかもしれませんが、サブパターン-[0-9.]*は受け入れられた回答から除外しました。これは正規表現ではなく、globパターンです。したがって、「ダッシュの後にゼロ個以上の数字またはドットが続く」という意味ではありません。代わりに、「ダッシュ、それに数字またはドット、それに続くもの」を意味します。 「何でも」は可能な限り最短の一致であり、最長ではありません。 (Bashは##および%%を提供して、最短ではなく最長のプレフィックスまたはサフィックスをトリミングします。)

4
tripleee

sedが* nixで利用可能であると想定できますが、mvコマンドを生成するためにsed -nをサポートするかどうかはわかりません。 (注:のみGNU sedはこれを行います。)

それでも、bash builtinsとsedを使用すると、これを行うためのシェル関数をすばやく作成できます。

sedrename() {
  if [ $# -gt 1 ]; then
    sed_pattern=$1
    shift
    for file in $(ls $@); do
      mv -v "$file" "$(sed $sed_pattern <<< $file)"
    done
  else
    echo "usage: $0 sed_pattern files..."
  fi
}

使用法

sedrename 's|\(.*\)\(-[0-9.]*\.pkg\)|\1\2|' *.pkg

before:

./Xft2-2.1.13.pkg
./jasper-1.900.1.pkg
./xorg-libXrandr-1.2.3.pkg

after:

./Xft2.pkg
./jasper.pkg
./xorg-libXrandr.pkg

ターゲットフォルダーの作成:

mvはターゲットフォルダーを自動的に作成しないため、sedrenameの初期バージョンを使用できません。

それはかなり小さな変更なので、その機能を含めることは素晴らしいことです:

Bashにはこの組み込み機能がないため、ユーティリティ関数abspath(または絶対パス)が必要です。

abspath () { case "$1" in
               /*)printf "%s\n" "$1";;
               *)printf "%s\n" "$PWD/$1";;
             esac; }

それができたら、新しいフォルダー構造を含むsed/renameパターンのターゲットフォルダーを生成できます。

これにより、ターゲットフォルダの名前が確実にわかります。名前を変更するときは、ターゲットファイル名で使用する必要があります。

# generate the rename target
target="$(sed $sed_pattern <<< $file)"

# Use absolute path of the rename target to make target folder structure
mkdir -p "$(dirname $(abspath $target))"

# finally move the file to the target name/folders
mv -v "$file" "$target"

完全なフォルダ対応スクリプトは次のとおりです...

sedrename() {
  if [ $# -gt 1 ]; then
    sed_pattern=$1
    shift
    for file in $(ls $@); do
      target="$(sed $sed_pattern <<< $file)"
      mkdir -p "$(dirname $(abspath $target))"
      mv -v "$file" "$target"
    done
  else
    echo "usage: $0 sed_pattern files..."
  fi
}

もちろん、特定のターゲットフォルダーがない場合でも機能します。

すべての曲をフォルダーに入れたい場合は、./Beethoven/を実行できます。

使用法

sedrename 's|Beethoven - |Beethoven/|g' *.mp3

before:

./Beethoven - Fur Elise.mp3
./Beethoven - Moonlight Sonata.mp3
./Beethoven - Ode to Joy.mp3
./Beethoven - Rage Over the Lost Penny.mp3

after:

./Beethoven/Fur Elise.mp3
./Beethoven/Moonlight Sonata.mp3
./Beethoven/Ode to Joy.mp3
./Beethoven/Rage Over the Lost Penny.mp3

ボーナスラウンド...

このスクリプトを使用して、フォルダーから単一のフォルダーにファイルを移動します。

一致するすべてのファイルを収集し、それらを現在のフォルダーに配置する場合、次のようにできます。

sedrename 's|.*/||' **/*.mp3

before:

./Beethoven/Fur Elise.mp3
./Beethoven/Moonlight Sonata.mp3
./Beethoven/Ode to Joy.mp3
./Beethoven/Rage Over the Lost Penny.mp3

after:

./Beethoven/ # (now empty)
./Fur Elise.mp3
./Moonlight Sonata.mp3
./Ode to Joy.mp3
./Rage Over the Lost Penny.mp3

Sed正規表現パターンに関する注意

このスクリプトでは通常のsedパターンルールが適用されますが、これらのパターンはPCRE(Perl Compatible Regular Expressions)ではありません。プラットフォームに応じてsed -rまたはsed -Eを使用して、拡張正規表現構文をsedできます。

Sedの基本および拡張正規表現パターンの完全な説明については、POSIX準拠のman re_formatを参照してください。

3
ocodo

これにはsedを使用した方が良いでしょう。

find . -type f -name "*.pkg" |
 sed -e 's/((.*)-[0-9.]*\.pkg)/\1 \2.pkg/g' |
 while read nameA nameB; do
    mv $nameA $nameB;
 done

正規表現を理解することは練習問題として残されます(スペースを含むファイル名を扱う場合と同様)

3
Javier

名前の変更は、この種のことを行うためのはるかに簡単なツールであることがわかりました。 OSXのHomebrewで見つけました

あなたの例では、私はそうします:

rename 's/\d*?\.\d*?\.\d*?//' *.pkg

「s」は代替を意味します。フォームはs/searchPattern/replacement/files_to_applyです。これには正規表現を使用する必要がありますが、少し勉強する必要がありますが、努力する価値があります。

3
Simon Katan

同じフォルダに複数の*.txtファイルの名前を.sqlに変更しました。以下は私のために働いた:

for i in \`ls *.txt | awk -F "." '{print $1}'\` ;do mv $i.txt $i.sql; done
1
Kumar Mashalkar

これは、

  • すべてが$ pkgで終わります
  • バージョン番号は常に「-」で始まります

.pkgを削除し、次に削除します-..

for x in $(ls); do echo $x $(echo $x | sed 's/\.pkg//g' | sed 's/-.*//g').pkg; done
1
Steve B.

この回答ありがとうございます。また、何らかの問題がありました。 .nzb.queuedファイルを.nzbファイルに移動します。ファイル名にスペースなどが含まれていたため、問題が解決しました。

find . -type f -name "*.nzb.queued" |
sed -ne "s/^\(\(.*\).nzb.queued\)$/mv -v \"\1\" \"\2.nzb\"/p" |
sh

Diomidis Spinellisの答えに基づいています。

正規表現は、ファイル名全体に対して1つのグループを作成し、.nzb.queuedの前の部分に対して1つのグループを作成してから、シェル移動コマンドを作成します。文字列を引用符で囲みます。これは、sedによって既に行われているため、シェルスクリプトでループを作成することも避けます。

0
Jerry Jacobs