面白い問題を出してくれてありがとう。これが私の解決策です。
#!/bin/bash
TABLE_FILE='file.table'
DIRECTORY='.'
PRETEND=true ## Set to false to change files.
declare -A RMAP=() FLAGS=() RENAMED=() NEWPATHS=()
declare -a ORDER=()
function check_directory {
if [[ ! -d $DIRECTORY ]]; then
echo "Directory does not exist: $DIRECTORY"
return 1
fi
return 0
}
function parse_table_file {
local -A F=() T=()
local -i I=0
if [[ ! -f $TABLE_FILE || ! -r $TABLE_FILE ]]; then
echo "Table file does not exist or is not readable: $TABLE_FILE"
return 1
fi
while read FROM TO; do
if [[ -n ${F[FROM]} ]]; then
echo "We can't have two the same sources: $FROM"
return 1
fi
if [[ -n ${T[TO]} ]]; then
echo "We can't have two them same replacements: $TO"
return 1
fi
RMAP[$FROM]=$TO ORDER[I++]=$FROM F[$FROM]=. T[$TO]=.
done < "$TABLE_FILE"
return 0
}
function rename {
local FROM=$1 TO=$2
if [[ ${FLAGS[$FROM]} != R ]]; then
if [[ ${FLAGS[$TO]} == H ]]; then
echo "Circular rename point detected: $FROM - $TO"
return 1
elif [[ -n ${RMAP[$TO]} ]]; then
FLAGS[$FROM]=H
rename "$TO" "${RMAP[$TO]}" || return 1
fi
local -a TARGETS=("$DIRECTORY/$FROM"*)
local NEWNAME NEWPATH
for TARGET in "${TARGETS[@]}"; do
NEWNAME=${TO}${TARGET##**"/${FROM}"}
NEWPATH=${DIRECTORY}/${NEWNAME}
echo "Renaming $TARGET to $NEWNAME ($NEWPATH)."
if [[ -n ${NEWPATHS[$NEWPATH]} ]]; then
echo "$TO has had same file matches as the previous ones: $NEWPATH through pattern ${NEWPATHS[$NEWPATH]}."
return 1
fi
if [[ -e $NEWPATH ]]; then
if [[ -n ${RENAMED[$NEWPATH]} ]]; then
if [[ $PRETEND == false ]]; then
echo "Can't rename $TARGET to $NEWNAME ($NEWPATH): file still exists and should have been renamed already with previous set ${RENAMED[$NEWPATH]}."
return 1
fi
else
echo "Can't rename $TARGET to $NEWNAME ($NEWPATH): file already exists and is not part of the renaming sequence."
return 1
fi
fi
if [[ $PRETEND == false ]]; then
mv "$TARGET" "$NEWPATH" || {
echo "Failed to rename $NEWNAME to $NEWPATH."
return 1
}
fi
NEWPATHS[$NEWPATH]=$TO
RENAMED[$TARGET]="$FROM - $TO"
done
FLAGS[$FROM]=R
fi
return 0
}
function process {
for FROM in "${ORDER[@]}"; do
rename "$FROM" "${RMAP[$FROM]}" || return 1
done
return 0
}
check_directory && parse_table_file && process
このスクリプトは、一時的な名前変更を必要とせずに競合を解決および検出するために再帰を利用します。また、名前を変更したふりをして、変更が既に行われている場合にどうなるかを確認し、間違いを防ぐこともできます。
例私はこれらのファイルを持っていました:
aaa (D)
aab (D)
aaa.txt (F)
次に、テーブルに次の行がありました。
aaa aab
aab aac
これは私の出力です:
Renaming ./aab to aac (./aac).
Renaming ./aaa to aab (./aab).
Renaming ./aaa.txt to aab.txt (./aab.txt).