Linuxファイルの名前をバッチで変更
1. 概要
Linuxでファイルの名前を変更する場合は、通常、mvコマンドを使用します。 ただし、 mv コマンドでは、ファイルの名前をバッチで変更することはできません。
このチュートリアルでは、バッチの名前変更のユースケースと、いくつかの異なる方法でそれらを解決する方法を見ていきます。
2. ツールの名前変更
名前の変更に役立つ3つの方法を選択しました。
- 名前の変更コマンド
- perl-renameツール
- awk | shアプローチ
まず、ツールをインストールする方法、またはツールがすでにディストリビューションで利用可能である可能性が高いかどうかを見てみましょう。
2.1. 名前の変更コマンド
rename コマンドは、util-linuxパッケージからのものです。 ファイル名に最初に出現するテキストのみを置き換えます。
util-linuxパッケージはkernel.orgによって配布される標準パッケージであるため、renameコマンドはデフォルトですべてのLinuxディストリビューションで使用できます。
この記事では、これを名前の変更と呼びます。
2.2. perl-renameコマンド
perl-rename コマンドは、最新のLinuxディストリビューションではデフォルトでは使用できません。
Ubuntuファミリのディストリビューションでは、perl-renameをapt-getとともにインストールできます。
root# apt-get install rename
RedHatファミリーディストリビューションにperl-renameをインストールするには、prenameパッケージをインストールする必要があります。
root# yum install prename
インストール後、perl-renameコマンドが/usr / bin /prenameにインストールされます。
Archlinuxから派生したディストリビューションでは、perl-renameをpacmanとともにインストールできます。
root# pacman -Syu perl-rename
Perl互換正規表現(PCRE)をサポートしているため、 perl-renameコマンドは、デフォルトの名前変更コマンドよりも強力であり、広く使用されています。
この記事では、これをprenameと呼びます。
2.3. awk | shアプローチ
awk自体はファイル名変更ツールではありません。 ただし、その強力なテキスト処理機能により、 awk を使用してファイル名を処理し、それらを「 mvoldNamenewName」コマンドに変換できます。
これらの「mv」コマンドをシェルにパイプして実行できます。
awkは最新のLinuxディストリビューションにプリインストールされているため、ルートのようなソフトウェアをインストールする権限がない場合は、これを使用してファイルの名前をバッチで変更できます。 prenameユーティリティ。 また、一部のシナリオでは、 awk は、prenameでは実行できない名前変更ジョブを実行できます。
名前変更シナリオの例では、GNU awkを使用します。
3. ファイル拡張子を変更する
最初のシナリオは一般的なシナリオです。 たとえば、多くの*。txtファイルがあります。
$ ls
file1.txt file2.txt file3.txt file4.txt file5.txt
すべての*。txtファイルの名前を*。logに変更するとします。
3.1. 名前の変更
コマンドrenameの作業は簡単です。 ただし、使用方法を説明する前に、まずrenameコマンドの2つの重要なオプションについて学習しましょう。
- -n:ドライラン、変更しないでください
- -v:冗長、名前が変更されたファイルを表示
-nv の2つのオプションを組み合わせると、 rename コマンドは、実際に変更を適用せずに行われる変更のみを表示します。
これは、変更を加える前に確認するのに非常に役立ちます。
$ rename -nv txt .log *.txt
`file1.txt' -> `file1..log'
`file2.txt' -> `file2..log'
`file3.txt' -> `file3..log'
`file4.txt' -> `file4..log'
`file5.txt' -> `file5..log'
この例では、コマンドラインを入力したときに、誤って置換「log」の前に余分なドットがありました。
「-nv」オプションを指定したrenameコマンドは、間違いを明確に示し、コマンドを修正する機会を与えてくれます。
することをお勧めします
次に、 rename コマンドを使用して、.txtファイルの名前を.logに変更しましょう。
$ rename .txt .log *.txt
$ ls
file1.log file2.log file3.log file4.log file5.log
renameコマンドは非常に簡単です。 各ファイル名で最初に出現するtxtを検索し、logに置き換えます。
または、findコマンドでrenameを使用して、特定のファイルをターゲットにすることもできます。
$ ls
log1-backup.txt log1.txt log2-backup.txt log2.txt log3.txt log4.txt
$ find . -iname "log*-backup.txt" -exec rename .txt .xml '{}' \;
$ ls
log1-backup.xml log1.txt log2-backup.xml log2.txt log3.txt log4.txt
-exec 引数は、 find に、一致するすべてのファイルに対して名前の変更を実行するように指示します。 この場合、「backup」を含む名前のすべてのファイルが対象になります。
「。」ということを覚えておいてください。 find コマンドの後は、現在のディレクトリを示します。
3.2. プレネーム
prenameコマンドは、Perlの検索と置換式に基づいてファイルの名前を変更します。 また、 -nv (ドライランおよび冗長)オプションもサポートしています。
prenameコマンドを使用してtxtファイルの名前を変更する方法を見てみましょう。
$ prename 's/[.]txt$/.log/' *.txt
$ ls
file1.log file2.log file3.log file4.log file5.log
この例では、正規表現マーカー $ を使用して、ファイル名の最後に一致する必要があることを示すことができました。
3.3. awk | sh
awkは強力なテキスト処理ユーティリティです。 awkで生成されたmvコマンドをシェルにパイプして、一括名前変更を行うことができます。
awk '...' | sh
awk には「ドライラン」オプションはありませんが、を削除すると[| sh “、awkは、生成されたすべてのmvコマンドを実行せずにstdoutに出力します。これは、renameおよび
find コマンドを使用して、ファイル名を入力としてawkにパイプできます。
$ find . -name "*.txt" | awk -v mvCmd='mv "%s" "%s"\n' \
'{ old = $0;
sub(/[.]txt$/,".log");
printf mvCmd,old,$0;
}'
mv "./file5.txt" "./file5.log"
mv "./file4.txt" "./file4.log"
mv "./file3.txt" "./file3.log"
mv "./file2.txt" "./file2.log"
mv "./file1.txt" "./file1.log"
シェルスクリプトを作成するときは、lsの出力を解析しないでください。これは、ファイル名に空白、タブ、または改行が含まれている可能性があるためです。 lsの出力ではそれらをうまく区別できません。
find コマンドを使用して、awkにファイル名を提供する必要があります。 awk はファイル名式を入力として受け取ることができますが、その式を使用してファイルの内容を読み取り、名前自体でテキスト処理を実行します。
を追加すると| コマンドに対してshを実行すると、すべての*。txtファイルの名前が*。logに変更されます。
$ find . -name "*.txt" | awk -v mvCmd='mv "%s" "%s"\n' \
'{ old=$0;
sub(/[.]txt$/,".log");
printf mvCmd,old,$0;
}' | sh
$ ls
file1.log file2.log file3.log file4.log file5.log
4. ファイル名の文字列を別の文字列に置き換える
この種の名前変更の問題に頻繁に直面します。 たとえば、次の*。txtファイルがあります。
$ ls -1
image1.txt
image2.txt
image3.txt
image4KeepMe.txt
image5KeepMe.txt
最初の3つのファイルでのみ、テキスト「image」を「picture」に置き換え、最後の2つのファイルは変更しないとします。
4.1. 名前の変更
rename コマンドを使用すると、問題が発生します。
$ rename -nv image picture *.txt
`image1.txt' -> `picture1.txt'
`image2.txt' -> `picture2.txt'
`image3.txt' -> `picture3.txt'
`image4KeepMe.txt' -> `picture4KeepMe.txt'
`image5KeepMe.txt' -> `picture5KeepMe.txt'
renameコマンドは正規表現パターンマッチングをサポートしていません。したがって、名前に「KeepMe」が含まれる最後の2つのファイルを単独で除外することはできません。
この問題を解決するために、globbingトリックを使用できます。
$ rename -nv image picture image?.txt
`image1.txt' -> `picture1.txt'
`image2.txt' -> `picture2.txt'
`image3.txt' -> `picture3.txt'
この場合、glob式は、名前が変更されたファイルを絞り込むのに役立ちました。
同様に、findとrenameを一緒に使用して、同じ目的を達成できます。
たとえば、「backup」を「ignore」に置き換えましょう。
$ ls *backup*
log1-backup.xml log2-backup.xml
$ find . -iname "*backup*" -exec rename backup ignore '{}' \;
$ ls
log1-ignore.xml log1.txt log2-ignore.xml log2.txt log3.txt log4.txt
4.2. プレネーム
prename コマンドは、より強力なPCREを提供します。 したがって、最後の2つのファイルを除外することは、prenameにとってまったく問題ではありません。
単純なネガティブ先読みで問題が解決します。
$ prename 's/image(?!.*KeepMe)/picture/' *.txt
$ ls -1
image4KeepMe.txt
image5KeepMe.txt
picture1.txt
picture2.txt
picture3.txt
4.3. awk | sh
正規表現は、awkプログラミングの基本的な部分です。 したがって、 awk は、最後の2つの「KeepMe」ファイルも簡単に除外できます。
$ find . -name "*.txt" | awk -v mvCmd='mv "%s" "%s"\n' \
'!/KeepMe/ {
old=$0;
sub(/image/,"picture");
printf mvCmd,old,$0;
}'| sh
$ ls -1
image4KeepMe.txt
image5KeepMe.txt
picture1.txt
picture2.txt
picture3.txt
5. ファイル名内の文字列のすべての出現箇所を別の文字列に置き換えます
以前は、ファイル名に含まれる1つの部分文字列を置き換えようとしていました。 それでは、すべてのオカレンスを置き換えたいときに何が起こるかを見てみましょう。
これらのファイルから始めましょう:
$ ls -1
igm1.igm
igm2_igm3.igm.zip
igm4_igm5_igm6.igm.zip
すべてのファイル名で出現するすべての「igm」を「img」に変更しましょう。 文字列「igm」の出現回数はファイル名によって異なることに注意してください。
5.1. 名前の変更
rename コマンドは、文字列の最初の出現のみを置き換えることをすでに学びました。 したがって、renameコマンドはこのシナリオを処理できません。
5.2. プレネーム
Perlの置換式は、「 g 」(グローバル)修飾子をサポートします。 これにより、マッチング演算子はパターンのすべてのオカレンスをマッチングできます。
$ prename 's/igm/img/g' *
$ ls -1
img1.img
img2_img3.img.zip
img4_img5_img6.img.zip
5.3. awk | sh
awkのsub 関数を使用して、最初に出現する文字列を置き換えました。
awkには別の関数gsubがあり、これはPerlの「g」修飾子と同じです。
$ find . -type f | awk -v mvCmd='mv "%s" "%s"\n' \
'{ old=$0;
gsub(/igm/,"img");
printf mvCmd,old,$0;
}' | sh
$ ls -1
img1.img
img2_img3.img.zip
img4_img5_img6.img.zip
6. ファイル名の数値をフォーマットする
このシナリオでは、ファイル名の数値をフォーマットしてみましょう。 たとえば、ディレクトリの下に3つの*。txtファイルがあります。
$ ls -1
afile-1.txt
bfile-10.txt
cfile-123.txt
リストを読みやすくするために、数値をフォーマットして先行ゼロを追加します。
- afile-1.txtはafile-001.txtになります
- bfile-10.txtはbfile-010.txtになります
- cfile-123.txtはその名前を保持します
6.1. 名前の変更
番号は動的であるため、renameコマンドは1つのコマンドで名前変更ジョブを実行できません。
6.2. プレネーム
Perlの置換式は、sprintf関数に「e」(eval)修飾子を提供します。
$ prename 's/\d+/sprintf("%03d","$&")/e' *.txt
$ ls -1
afile-001.txt
bfile-010.txt
cfile-123.txt
6.3. awk | sh
awkにはsprintf機能もあります。
ただし、prenameと同じように使用することはできません。 代わりに、 sub 関数を呼び出す前に、sprintfで数値をフォーマットする必要があります。
$ find . -name "*.txt" | awk -F'[.-]' -v mvCmd='mv -n "%s" "%s"\n' \
'{ num=sprintf("%03d", $(NF-1));
old=$0;
sub(/[0-9]+/,num);
printf mvCmd,old,$0;
}'
$ ls -1
afile-001.txt
bfile-010.txt
cfile-123.txt
ここでは、 -F'[.-]’ を使用して、ファイル名を「。」または「 –」で分割します。 この場合、最後から2番目のフィールド $(NF-1)がフォーマットする数値です。
7. ファイル名の大文字と小文字を変更する
このシナリオでは、ファイル名のすべての大文字を小文字に変換してみましょう。
$ ls -1
INSTRUCTION.TxT
Query.SQL
ReadMe.MD
7.1. 名前の変更
別の動的な名前変更シナリオ。これはrenameコマンドのジョブではありません。
7.2. プレネーム
Perlには、入力文字列を小文字に変換する lc ( lowercase )関数があります。 lc関数と「e」修飾子を併用すると、この問題を解決できます。
$ prename 's/.*/lc("$&")/e' *
$ ls -1
instruction.txt
query.sql
readme.md
lc 関数を除いて、Perlの音訳演算子「y」を使用して、すべての大文字を小文字に変換することもできます。
$ prename 'y/A-Z/a-z/' *
$ ls -1
instruction.txt
query.sql
readme.md
7.3. awk | sh
awk には、特にケース変換用の2つの機能があります。tolowerとtoupperです。 ここでtolowerを使用しましょう:
$ find . -type f | awk -v mvCmd='mv "%s" "%s"\n' \
'{ new=tolower($0);
printf mvCmd,$0,new;
}' | sh
$ ls -1
instruction.txt
query.sql
readme.md
8. ファイル名の文字列を入れ替える
ディレクトリの下に多くのシステムログファイルがあるとしましょう。 各ファイル名には、DD-MM-YYYYの形式の日付文字列が含まれています。
$ ls -1
08-08-1992_system.log
18-11-1976_system.log
29-11-2019_system.log
DD-MM-YYYY形式をISO日付形式に変換してファイルの名前を変更します:YYYY-MM-DD。
8.1. 名前の変更
別の動的な名前変更シナリオ。これはrenameコマンドのジョブではありません。
8.2. プレネーム
ここでは、後方参照とキャプチャグループを使用して、目標を達成できます。
$ perl-rename -nv 's/(\d\d)-(\d\d)-(\d{4})/\3-\2-\1/' *.log
$ ls -1
1976-11-18_system.log
1992-08-08_system.log
2019-11-29_system.log
正規表現で()によって定義される3つのキャプチャグループは、置換式でそれらの番号 \ 1、\ 2 、および \3によって参照されます。 。
8.3. awk | sh
GNU awkの優れたgensub関数を使用すると、後方参照も処理できます。
$ find . -name "*.log" | awk -v mvCmd='mv "%s" "%s"\n' \
'{ new=gensub(/([0-9]{2})-([0-9]{2})-([0-9]{4})/, "\\3-\\2-\\1", "g");
printf mvCmd,$0,new;
}' | sh
$ ls -1
1976-11-18_system.log
1992-08-08_system.log
2019-11-29_system.log
9. UnixタイムスタンプをISO日付形式に変換する
このシナリオでは、より詳細な日付形式の変換について説明します。
ファイルを見てみましょう:
$ ls -1
app_1575212161.log
app_217189800.log
app_713302200.log
各ファイル名にはUnixタイムスタンプが含まれています。UnixタイムスタンプをISO日付形式に変換したいと思います。
9.1. 名前の変更
それでも、renameコマンドはこのジョブを実行できません。
9.2. プレネーム
Perlの検索でPerl式を評価し、式を「e」修飾子に置き換えることができることを学びました。 このトリックをもう一度使用して、この問題を解決します。
$ prename 'use POSIX qw(strftime);s/\d+/strftime "%FT%H:%M:%S", localtime($&)/e' *.log
$ ls -1
app_1976-11-18T19:30:00.log
app_1992-08-08T21:30:00.log
app_2019-12-01T15:56:01.log
localtime 関数は、UnixタイムスタンプをPerlの時間タイプに変換します。 次に、POSIXモジュールのPerlのstrftime関数は、時刻をISO日付形式に変換します。
9.3. awk | sh
GNU awkにはstrftime関数もあります。 Unixタイムスタンプからさまざまな日付形式を取得するのに役立ちます。
$ find . -name "*.log" | awk -F'[_.]' -v mvCmd='mv "%s" "%s"\n' \
'{ old=$0;
sub(/[0-9]+/,strftime("%FT%T", $(NF-1)));
printf mvCmd, old, $0;
}' | sh
$ ls -1
app_1976-11-18T19:30:00.log
app_1992-08-08T21:30:00.log
app_2019-12-01T15:56:01.log
GNU awk’のstrftime関数を使用することに加えて、awkを使用した代替ソリューションも試す価値があります。
awkは外部コマンドを呼び出し、さらに処理するために出力を取得できます。 getline 式を使用してコマンドの出力を取得し、それを変数に割り当てることができます。
External_Command | getline variable
date コマンドを使用してUnixタイムスタンプをISO形式に変換してから、Unixタイムスタンプをその出力に置き換えてみましょう。
$ find . -name "*.log"|awk -F'[_.]' -v mvCmd='mv "%s" "%s"\n' \
'{ old=$0;
"date +%FT%T -d @"$(NF-1)|getline isoFmt;
gsub(/[0-9]+/,isoFmt);
printf mvCmd, old, $0;
}' | sh
$ ls -1
app_1976-11-18T19:30:00.log
app_1992-08-08T21:30:00.log
app_2019-12-01T15:56:01.log
外部コマンドと連携する機能により、awkはより強力になります。 たとえば、さらに複雑な名前変更ジョブを実行できます。
- ファイル名にファイルのmd5ハッシュを追加します( md5sum コマンド)
- ファイルの内容に何らかのパターンが含まれている場合は、ファイル名にいくつかのマーカーを追加します( grep コマンド)
- ファイル名のドメイン名をIPアドレスに変換します(nslookupまたはhostコマンド)
10. ファイル名で数値を動的にフォーマットする
先ほど、数値フォーマットのシナリオを見ました。 事前定義された固定形式をに渡すことで、先行ゼロを追加できることを学びました。 sprintf 関数—たとえば、 sprintf(“ %03d”、” $&”) 。
ただし、特にファイルの数が多い場合は、ファイルを手動で調べて先行ゼロの数を判別したくない場合があります。
処理中のファイルに基づいて、ファイルの名前変更中に数値形式を自動的に計算する方法があります。
前に使用した例にいくつかの新しいファイルを追加しましょう。
$ ls -1
afile-1.txt
bfile-10.txt
cfile-123.txt
dfile-4711.txt
efile-20191201.txt
renameもprenameも、これらすべての番号を同じ長にするために必要な正しいパディングを検出できません。
10.1. awk | sh
awk は、 find コマンドからファイル名の完全なリストを取得するため、awkで問題を解決できます。
$ find . -name "*.txt" | awk -F'[-.]' -v mvCmd='mv -n "%s" "%s"\n' \
'{ num = $(NF-1);
files[$0] = num;
max = num > max? num : max;
}
END { width = length(max);
for(name in files) {
old = new = name;
formattedNum = sprintf("%0*d", width, files[name]);
sub(files[name],formattedNum,new);
printf mvCmd,old,new;
}
}' | sh
$ ls -1
afile-00000001.txt
bfile-00000010.txt
cfile-00000123.txt
dfile-00004711.txt
efile-20191201.txt
awk スクリプトを1行ずつ見ていき、そこで何が起こっているかを見てみましょう。
- findの結果をawkにパイプし、フィールドセパレーターとmvコマンドテンプレートを定義します
- ファイル名ごとに、最後から2番目のフィールドである番号を抽出し、num変数に保存します。
- files という名前のハッシュテーブルを作成します—キーは完全なファイル名( $ 0 )であり、値はnum変数です
- すべてのファイル名から最大のnumを見つけて、max変数に保存します
- awk はすべてのファイル名を調べ、必要なすべてのデータをfilesおよびmax変数に保存しました
- 最大数の幅を計算します( max )
- ハッシュテーブルファイルの各要素に対して、awkはmvコマンドを生成します
- 名前を変更するために名前のコピーを作成します
- 現在の名前の数値をフォーマットします
- ファイル名の元の番号( new )をformattedNumに置き換えます
- mvコマンドを生成します
- ループを終了します
- 最後に、生成されたmvコマンドをshにパイプします
10.2. awk | 汎用の名前変更ソリューションとしてのsh
簡単な名前変更の問題に対するawkソリューションは、prenameなどの他の名前変更ツールほどコンパクトに見えない場合があります。
ただし、 awk は非常に便利で、学ぶ価値があります。 これは、その強力なスクリプト言語が、ほぼすべての種類の一括名前変更の問題を解決できるためです。
また、 awk は、ファイルの名前を変更するだけではありません。
11. 結論
この記事では、いくつかの一般的な名前変更ツールとその入手方法について説明しました。
次に、いくつかの一般的な名前変更シナリオを確認し、可能な場合は各ツールでそれらを解決する方法について説明しました。 rename は柔軟性が最も低く、 prename はPerl式を使用して、最も複雑な問題を除くすべてを解決できることがわかりました。
最後に、スクリプト化されたメソッドとして、awkとshが最も強力なソリューションであり、ほとんどの状況で利用できることを確認しました。