TL;DR
test.sh
#!/bin/bash
#=== 関数定義 ============================================
# 引数で渡されたファイルが存在するか確認し、既に存在する場合は
# .bkp1, .bkp2 ... というファイル名で空きを探す関数。
# 出力は標準出力に返される。
# 引数で渡されたファイルが存在しない場合は異常終了する。
function getBackupableFileName {
## 引数処理
# 引数1 探索するファイルのパス
local file_path=$1
## 関数内定数
# バックアップ用ファイルの拡張子名(ここに数字の1, 2...が着く)
local BACKUP_EXT=bkp
## 関数本体
# 引数で渡したファイルが存在しなかったら何も返さず異常終了
if [ ! -e "$file_path" ]; then
return 1
fi
# 引数で渡したファイルが既に存在するため、空き番号を探す
local n=1
# バックアップ用拡張子を追加したファイルが存在するかをチェック
while [ -e "$file_path.$BACKUP_EXT$n" ]; do
# 既に存在するので番号を1ずつ加算
n=$(( n + 1 ))
done
# 標準出力に結果を吐き出す。
echo "$file_path.$BACKUP_EXT$n"
}
#=== スクリプト本体 ========================================
# 出力ファイルへのパス(例)
OUTPUT_FILE=/path/to/output.file
# 同名の出力ファイルが既に存在した場合、上書きされないようにリネームして取っておく
if BKP_FILE=$(getBackupableFileName "$OUTPUT_FILE"); then
mv "$OUTPUT_FILE" "$BKP_FILE"
fi
技術的に忘れそうなこと
主に自分が忘れるのでメモしておく。
Bash のコマンド置換の終了ステータス
terminal
$ VALUE=$(true)
$ echo $?
0
$ VALUE=$(false)
$ echo $?
1
コマンド置換( $(コマンド)
のやつ)を変数の代入に利用した場合の終了ステータスはコマンドの終了ステータスになる。
切り抜き
# 同名の出力ファイルが既に存在した場合, リネームして取っておく.
if BKP_FILE=$(getBackupableFileName "$OUTPUT_FILE"); then
mv -v "$OUTPUT_FILE" "$BKP_FILE"
fi
if
はコマンドの終了ステータスに従って分岐するので、ここではgetBackupableFileName
が成功(exit 0
)したらmv
コマンドでリネームしている。
失敗(exit 1
)したら処理を行わずスキップする。
別案
ここでは.bkp*
がないか1つ1つ存在チェック([ -e ファイルパス ]
)している。
想像だが、恐らく.bkp100
あたりを超えると実行時間に悪影響が出る。
こういう場合は一旦ls
コマンドの出力を変数に確保しておき、その文字列から存在チェックをした方が高速化が見込める。
BKP_FILE_LIST=$(ls -1 "$file_path.${BACKUP_EXT}*") || :
ただし、今回の用途では.bkp3
あたりが関の山であり、その場合は[ -e ファイルパス ]
の方が早い。
用途によって切り替えるのが吉。