How to recursively rename files and folder with iconv from Bash
質問
I have been trying to recursively rename files AND folders with iconv without success, the files are correctly renamed but folders dont.
What I use for files is (works perfect):
find . -name * -depth \ -exec bash -c 'mv "$1" "${1%/*}/$(iconv -f UTF8 -t ASCII//TRANSLIT <<< ${1##*/})"' -- {} \;
What I tried for files AND folders (fail: Only rename folders):
find . -exec bash -c 'mv "$1" "$(iconv -f UTF8 -t ASCII//TRANSLIT <<< $1)"' -- {} \;
ORIGINAL problem: I just want to bulk rename lots of files to make them "web friendly", thinks like removing spaces, weird characters and so on, currently I have
find . -name '*' -depth \
| while read f ;
do
mv -i "$f" "$(dirname "$f")/$(basename "$f"|tr -s ' ' _|tr -d "'"|tr -d ","|tr - _|tr "&" "y"|tr "@" "a")" ;
done
Is there any way to do the tr stuff above and the iconv at a single run? because I am talking around 300,000 files to rename, I would like to avoid a second search if possible.
If needed, I am working with Bash 4.2.24
Thanks in advance.
解決
I think the following does everything you want in one pass.
# Update: if this doesn't work, use read -d '' instead
find . -print0 | while IFS= read -d '$\000' f ;
do
orig_f="$f"
# Below is pure bash. You can replace with tr if you like
# f="$( echo $f | tr -d ,\' | tr "$'&'@- " "ya__" )"
f="${f// /_}" # Replace spaces with _
f="${f//\'}" # Remove single quote
f="${f//-/_}" # Replace - with _
f="${f//,}" # Remove commas
f="${f//&/y}" # Replace ampersand with y
f="${f//@/a}" # Replace at sign with a
f=$( iconv -f UTF8 -t ASCII//TRANSLIT <<< "$f" )
new_dir="$(dirname $f)"
new_f="$(basename $f)"
mkdir -p "$new_dir"
mv -i "$orig_f" "$new_dir/$new_f"
done
The find
command (no real options needed, other than -print0
to handle filenames with spaces) will send null-separated file names to the while
loop (and someone will correct my errors there, no doubt). A long list of assignments utilizing parameter expansion removes/replaces various characters; I include what I think is the equivalent pipeline using tr
as a comment. Then we run the filename through iconv
to deal with character set issues. Finally, we split the name into its path and filename components, since we may have to make a new directory before executing the mv
.
他のヒント
Here is an update I offer after chepner's answer to avoid nesting bugs. Reverse the output of find
with tac
to act on folders content before the folders themselves. This way, there is no need to mkdir
anymore:
echo "renaming:"
find . -print0 | tac -s '' | while IFS= read -d '' f ;
do
Odir=$(dirname "$f") # original location
Ofile=$(basename "$f") # original filename
newFile=$Ofile
# remove unwanted characters
newFile=$(echo $newFile | tr -d ",'\"?()[]{}\\!")
newFile="${newFile// /_}" # Replace spaces with _
newFile="${newFile//&/n}" # Replace ampersand with n
newFile="${newFile//@/a}" # Replace at sign with a
newFile=$( iconv -f UTF8 -t ASCII//TRANSLIT <<< "$newFile" )
if [[ "$Ofile" != "$newFile" ]]; then # act if something has changed
echo "$Odir/$Ofile to"
echo "$Odir/$newFile"
mv -i "$Odir/$Ofile" "$Odir/$newFile"
echo ""
fi
done
echo "done."
Enjoy ;)