user1290793 user1290793 - 1 month ago 8
Linux Question

Replace double quotes alongside other invalid windows filename characters in filenames on linux

I have files on an ubuntu 14.04 linux machine that have invalid windows characters such as <>?:"|* etc. in a directory. I wish to remove these invalid windows characters so that they may be viewable from a windows machine as well.

Eg: the following are a couple of files in the directory:

file "1".html
file "asdf".txt


The expected output after renaming should be: (essentially, it renames the invalid characters with a single underscore)

file _1_.html
file _asdf_.txt


I've been running the command from Find files with illegal windows characters in the name on Linux (modifying it slightly):

find . -name "*[<>\\|?:\"*]*" -exec bash -c 'x="{}"; y=$(sed "s/[<>\\|?:\"*]\+/_/g" <<< "$x") && echo "renaming" "$x" "-->" "$y" && mv "$x" "$y" ' \;


But, the bash command above fails on files with double quotes in them. it works fine for the other invalid characters.

Can you help fix this script? Thanks in advance.

Answer

Using bash parameter expansion

$ touch 'file "1".html' 'file "asdf".txt' 'a<b' 'f?r' 'e*w' 'z|e' 'w:r' 'b>a'

$ ls
a<b  b>a  e*w  file "1".html  file "asdf".txt  f?r  w:r  z|e

$ find -name "*[<>\\|?:\"*]*" -exec bash -c 'echo mv "$0" "${0//[<>\\|?:\"*]/_}"' {} \;
mv ./z|e ./z_e
mv ./file "asdf".txt ./file _asdf_.txt
mv ./a<b ./a_b
mv ./file "1".html ./file _1_.html
mv ./e*w ./e_w
mv ./w:r ./w_r
mv ./f?r ./f_r
mv ./b>a ./b_a

$ find -name "*[<>\\|?:\"*]*" -exec bash -c 'mv "$0" "${0//[<>\\|?:\"*]/_}"' {} \;
$ ls
a_b  b_a  e_w  file _1_.html  file _asdf_.txt  f_r  w_r  z_e


To use extglob

$ touch 'tmp::<>|asdf.txt'
$ find -name "*[<>\\|?:\"*]*" -exec bash -c 'shopt -s extglob; echo mv "$0" "${0//+([<>\\|?:\"*])/_}"' {} \;
mv ./tmp::<>|asdf.txt ./tmp_asdf.txt


With perl based rename

$ find -name "*[<>\\|?:\"*]*" -exec rename 's/[<>\\|?:\"*]/_/g' {} +
$ ls
a_b  b_a  e_w  file _1_.html  file _asdf_.txt  f_r  w_r  z_e

Use rename -n for dry run without actually renaming the files

$ touch 'tmp::<>|asdf.txt'
$ find -name "*[<>\\|?:\"*]*" -exec rename -n 's/[<>\\|?:\"*]+/_/g' {} +
rename(./tmp::<>|asdf.txt, ./tmp_asdf.txt)