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


101

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

例:

Xft2-2.1.13.pkg なる Xft2.pkg

jasper-1.900.1.pkg なる jasper.pkg

xorg-libXrandr-1.2.3.pkg なる xorg-libXrandr.pkg


1
私はこれを、一度だけ使用できるロットのスクリプトとして定期的に使用するつもりです。私が使用するすべてのシステムにはbashが含まれているので、非常に便利なbashismを恐れていません。
Jeremy L

回答:


190

あなたはbashのパラメータ拡張機能を使うことができます

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

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


1
私も気に入っていますが、bash固有の機能を使用しています...通常は「標準」shを優先して使用しません。
ディエゴセビリア

2
使い捨ての1行のインタラクティブなものでは、sed-fuの代わりにこれを常に使用します。それが台本だったら、うん、バシズムを避けなさい。
richq 2009年

コードで見つけた1つの問題:ファイルの名前が既に変更されていて、再度実行するとどうなりますか?自分のサブディレクトリに移動しようとすると警告が表示されます。
Jeremy L

1
再実行エラーを回避するために、「. pkg」の部分で同じパターンを使用できます。つまり、「for i in *-[0-9。] .pkg; do mv $ i $ {i /-[0-9 。] *。pkg / .pkg};完了 "。しかし、エラーは無害です(同じファイルに移動)。
richq 2009年

3
bashソリューションではありませんが、perlがインストールされている場合は、バッチの名前を変更するための便利なrenameコマンドが提供されますrename "s/-[0-9.]*//" *.pkg
Rufus

33

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

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では、正規表現のグループ化シーケンスは、単一の大括弧and ではなく、大括弧の前にバックスラッシュ\(and が続く ことに注意してください。\)()


私の素朴さは許しますが、バックスラッシュで括弧をエスケープしないと、それらはリテラル文字として扱われますか?
devios1 2014年

2
正規表現グループ化配列はなく、単一のブラケットよりも、(あるsedの。
ディオミディス・スピネリス

私にとってはうまくいきませんでした。あなたの助けを借りて喜んでいます... FROMファイルのサフィックスがTOファイルに追加されます:$ find。タイプd | sed -n 's /(.*)\/(.*)anysoftkeyboard(。*)/ mv "\ 1 \ / \ 2anysoftkeyboard \ 3" "\ 1 \ / \ 2effectedkeyboard \ 3" / p' | sh >> >>>>> OUTPUT >>>> mv:./base/src/main/java/com/anysoftkeyboard/base/dictionariesを./base/src/main/java/com/effectedkeyboard/base/dictionaries/dictionariesに名前変更:そのようなファイルやディレクトリはありません
Vitali Pom

かっこ内のバックスラッシュがないようです。
Diomidis Spinellis 2018年

1
lsスクリプトでは使用しないでください。最初の多様printf '%s\n' * | sed ...性は、いくつかの創造性で達成することができ、おそらくあなたもそれを取り除くことができsedます。Bashにprintfは、'%q'リテラルシェルメタキャラクターを含むファイル名がある場合に役立つフォーマットコードが用意されています。
tripleee

10

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

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

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

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


それらはすべて同じディレクトリにあります。あなたはrqの答えをどう思いますか?
ジェレミーL

私はbash固有の機能を使用したくないが、より標準的なshを使用したい。
ディエゴセビリア

1
これは、名前をすばやく変更するためのものであり、次世代のクロスプラットフォームであるポータブルエンタープライズアプリケーションを作成するためのものではないと
思いました

1
ははは、まあまあ...私のような携帯性志向の男から抜け出して:)
Diego Sevilla

これは3番目の例であるxorg-libXrandr(名前にハイフンが使用されている)を説明していません。
ジェレミーL

5

以下は、現在受け入れられている回答に相当する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.]*誤解を招くかもしれませんが、私はサブパターンを受け入れられた回答から遠ざけました。これは正規表現ではなく、グロブパターンです。したがって、「ダッシュの後に0個以上の数字またはドットが続く」という意味ではありません。代わりに、「ダッシュの後に数字またはドットが続き、その後に何かが続く」ことを意味します。「何でも」は可能な最短の一致であり、最長ではありません。(バッシュの申し出##%%かなり短いよりも、可能な限り最長の接頭辞または接尾辞をトリミングします。)


5

sed任意の* nixで使用できると想定できますが、sed -nmvコマンドの生成をサポートするかどうかはわかりません。(注:sedこれを行うのはGNUだけです。)

それでも、組み込みのbashと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

これはかなり小さな変更なので、その機能を含めるとよいでしょう。

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

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 regexパターンに関する注意

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

man re_formatsedの基本および拡張正規表現パターンの詳細については、POSIX準拠を参照してください。


4

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

あなたの例では、私はやります:

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

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


同意する。使用して時折事については、forおよびsedなど細かいですが、それだけで使用することに速くずっと簡単だとrenameあなた自身が、多くの場合、これをやって見つければ。
argentum2f

あなたは期間を逃れていますか?
ビッグマネー

問題は、これが移植性がないことです。すべてのプラットフォームにrenameコマンドがあるわけではありません。さらに、機能、構文、オプションが異なる renameコマンドがいくつかあるものもあります。これrenameはかなり人気のあるPerlのように見えます。しかし、答えは本当にこれを明確にするはずです。
トリプリー

3

これのより良い使用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

正規表現の計算は、演習として残します(スペースを含むファイル名の扱いと同様)。


ありがとう:名前にスペースは使用されません。
Jeremy L

1

これは、

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

.pkgを取り除き、次に取り除きます。

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

名前にハイフンが含まれているパッケージがあります(3番目の例を参照)。これはコードに影響を与えますか?
ジェレミーL

1
を使用して、複数のsed式を実行できます-e。例えばsed -e 's/\.pkg//g' -e 's/-.*//g'
tacotuesday 2017

ls出力解析して変数引用しないでください。シェルスクリプトの一般的な問題とアンチパターンの診断については、shellcheck.netも参照してください。
tripleee

1

同じフォルダ*.txtにあるように、名前を変更する複数のファイルがありました.sql。以下は私のために働きました:

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

2
OPの実際のユースケースに抽象化することで、回答を改善できます。
dakab 2015年

これには複数の問題と、明らかな構文エラーがあります。はじめにshellcheck.netを参照してください。
tripleee

0

この回答をありがとうございます。私もある種の問題を抱えていました。.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によって既に行われているため、シェルスクリプトでループを作成することも回避します。


これはGNU sedにのみ当てはまることに注意してください。BSDでは、sedは-n同じ方法でオプションを解釈しません。
ocodo 2018年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.