連番のファイル群を参照する時に、ソートが意図した通りにならなかった事は無いでしょうか? 1 の後は 2 3 と連番になって欲しいけれど、1 の後が 10 11 .. 、2 の後が 20 21 … となってしまう。ls コマンドは、あくまで文字コードの順番でソートされるので数字の番号を意識してくれません。しかし番号の桁数を1 -> 001 や、10 -> 010 と揃えれば、文字コードの順番に番号が連番にソートされるようになります。しかしこの手のファイルはディレクトリに沢山あって手動で1つずつ変更するのは手間がかかります。今回はいくつかのパターンに対応したファイル名に含まれている番号の桁数をそろえるスクリプトを紹介したいと思います。
$ ls
1.txt 10.txt 11.txt 2.txt 3.txt 4.txt 5.txt 6.txt 7.txt 8.txt 9.txt
$ ls
file_1.txt file_10.txt file_11.txt file_2.txt file_3.txt file_4.txt file_5.txt file_6.txt file_7.txt file_8.txt file_9.txt
$ ls -1
1.txt
10.txt
11.txt
2.txt
3.txt
:
実行例
# ファイルリストを表示
$ ls
1.txt 10.txt 2.txt P_20220401_1.jpg P_20220401_10.jpg P_20220401_2.jpg file_1.txt file_10.txt file_2.txt filename_sort.sh
# スクリプトを実行
$ ./filename_sort.sh
1.txt -> 001.txt
10.txt -> 010.txt
2.txt -> 002.txt
P_20220401_1.jpg -> P_20220401_001.jpg
P_20220401_10.jpg -> P_20220401_010.jpg
P_20220401_2.jpg -> P_20220401_002.jpg
file_1.txt -> file_001.txt
file_10.txt -> file_010.txt
file_2.txt -> file_002.txt
# 変更内容がよければ yes と入力
ファイル名を変更しますか?(Yes/No) yes
mv 1.txt 001.txt
mv 10.txt 010.txt
mv 2.txt 002.txt
mv P_20220401_1.jpg P_20220401_001.jpg
mv P_20220401_10.jpg P_20220401_010.jpg
mv P_20220401_2.jpg P_20220401_002.jpg
mv file_1.txt file_001.txt
mv file_10.txt file_010.txt
mv file_2.txt file_002.txt
# ファイルリストが意図した通りの順番になっているのか確認
$ ls
001.txt 002.txt 010.txt P_20220401_001.jpg P_20220401_002.jpg P_20220401_010.jpg file_001.txt file_002.txt file_010.txt filename_sort.sh
filename_sort.sh
#!/bin/bash
# デリミタを改行に設定
IFS=$'\n'
# ループ終了フラグ
END_FLAG=1
# 変更フラグ
MV_FLAG=0
while [ ${END_FLAG} = "1" ];
do
# 数字を含むファイルを検索 file_XXX.txt XXX.txt P_20220401_XX.jpg などを対象
for line in `ls |grep -P '[^0-9]*[^0-9]*[0-9]+\.[a-zA-Z]+$'`
do
# デリミタをタブにしてファイル名を抽出 file_XXX.txt -> file XXX .txt
TMP_FILENAME=`echo "${line}"|sed -r 's/([^0-9]*[^0-9]*)([0-9]+)(\.[a-zA-Z]+$)/\1\t\2\t\3/'`
# ファイル番号を取得
FILENO=`echo "${TMP_FILENAME}" | awk -F "\t" '{print $2}'`
# 桁数を 3 桁に揃える
FILENO=`printf %03d ${FILENO}`
# 数字以外のファイル名を取得
FILENAME=`echo "${TMP_FILENAME}" | awk -F "\t" '{print $1}'`
# ファイルの拡張子を取得
EXT=`echo "${TMP_FILENAME}" | awk -F "\t" '{print $3}'`
if [ ${MV_FLAG} = "1" ]; then
# ファイル名を変更
echo "mv ${line} ${FILENAME}${FILENO}${EXT}"
mv "${line}" "${FILENAME}${FILENO}${EXT}"
else
# 変更候補リストを表示
echo "$line -> $FILENAME$FILENO$EXT"
fi
done
# ループ終了
if [ ${MV_FLAG} = 1 ]; then
break
fi
# 変更確認を表示
echo -n "ファイル名を変更しますか?(Yes/No) "
read MV_OK
case $MV_OK in
# yes と入力されたら MV_FLAG を立て、ループに戻る。
[Yy][Ee][Ss] | [Yy][Ee] | [Yy] )
MV_FLAG=1
;;
* )
# END_FLAG を下げてループを終了
echo "キャンセルされました。"
END_FLAG=0
;;
esac
done