Ubuntu – Rename Files & Folders With Special Characters

command linefilenamerenamespecial characters

Struggling with this one and Googling it doesn't seem to be giving me solution that works.

I have a folder named…

file å?? name.txt

for example and to avoid problems, I want to rename it however I'm struggling to work out how. I've tried using ' ' and I also tried using \ before each special character e.g. 'file \å\?\? name \(2008\).txt' but I just get the following error…

-bash: cd: file \å\?\? name \(2008\).txt : No such file or directory

Is this improper use of \ or do I need an alternative approach?

Thanks

Best Answer

The non-ASCII characters are not the problem, your shell can deal with å perfectly well. The issue is that your file is not actually named å??. If it were, rm 'å??' or even rm å?? would have worked.

You assume it's å?? because that's what ls shows, and that's a reasonable assumption, but ls will show various things as ?. For example:

$ mkdir "file å"\?\?" name.txt" "file å"$'\n'$'\n'" name.txt" "file å"$'\t'$'\t'" name.txt" "file å"$'\r'$'\r'" name.txt" "file å"$'\b'$'\b'" name.txt" "file å"$'\v'$'\v'" name.txt"

$ ls -l
total 24K
drwxr-xr-x 2 terdon users 4.0K Nov  8 13:02 file å?? name.txt
drwxr-xr-x 2 terdon users 4.0K Nov  8 13:02 file å?? name.txt
drwxr-xr-x 2 terdon users 4.0K Nov  8 13:02 file å?? name.txt
drwxr-xr-x 2 terdon users 4.0K Nov  8 13:01 file å?? name.txt
drwxr-xr-x 2 terdon users 4.0K Nov  8 13:02 file å?? name.txt
drwxr-xr-x 2 terdon users 4.0K Nov  8 13:02 file å?? name.txt

As you can see above, newlines, tabs, carriage returns, bells and vertical tabs (among others) are all shown as ?. Only the first file/directory of the ones created above actually has ? in its name. We can confirm this with ls -b:

$ ls -lb
total 24
drwxr-xr-x 2 terdon users 4096 Nov  8 13:02 file\ å??\ name.txt
drwxr-xr-x 2 terdon users 4096 Nov  8 13:02 file\ å\b\b\ name.txt
drwxr-xr-x 2 terdon users 4096 Nov  8 13:02 file\ å\t\t\ name.txt
drwxr-xr-x 2 terdon users 4096 Nov  8 13:01 file\ å\n\n\ name.txt
drwxr-xr-x 2 terdon users 4096 Nov  8 13:02 file\ å\v\v\ name.txt
drwxr-xr-x 2 terdon users 4096 Nov  8 13:02 file\ å\r\r\ name.txt

So, you can either run ls -b to get the right file name and then use ANSI C quoting to rename it:

mv å$'\r'$'\r' newname

Alternatively, you can use a glob to match all files/directories whose name starts with file å (note: this will only work if you just have one) :

mv "file å*" newname

Or, rename all files/directories whose name contains non alphanumeric characters (again, only useful for cases where you have a single such case):

shopt -s extglob  ## turn on extended globbing
mv !(*([[:graph:]])) newname

The strange !(*([[:graph:]])) needs some explaining. extglob enables extended globbing which lets us use !(foo) to match "not foo". The [[:graph:]] character class matches all printable characters (not tabs, newlines etc.) Therefore, the negated match !(*([[:graph:]])) will match all file/dirnames with non-printing characters.

If you need to deal with more than one such case, use a loop. Something like:

for dir in !(*([[:graph:]])); do 
     mv "$dir" "${dir//[^[:graph:]]/_}"; 
done

The ${dir//[^[:graph:]]/_} is the directory name with all non-printing characters replaced by _. The problem with this approach is that you can have different source directories which will end up with the same name (e.g. foo\n and foo\t will both become foo_). If that is a problem, just rename using a counter as well:

a=0; for dir in !(*([[:graph:]])); do 
    ((a++)); 
     mv "$dir" "${dir//[^[:graph:]]/_}$a"
done

That would result in:

drwxr-xr-x 2 terdon users 4096 Nov  8 13:08 file_å___name.txt1
drwxr-xr-x 2 terdon users 4096 Nov  8 13:08 file_å___name.txt2
drwxr-xr-x 2 terdon users 4096 Nov  8 13:08 file_å___name.txt3
drwxr-xr-x 2 terdon users 4096 Nov  8 13:08 file_å??_name.txt4
drwxr-xr-x 2 terdon users 4096 Nov  8 13:08 file_å___name.txt5
drwxr-xr-x 2 terdon users 4096 Nov  8 13:08 file_å___name.txt6
Related Question