Pregunta

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.

¿Fue útil?

Solución

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.

Otros consejos

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 ;)

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top