Ubuntu: How to rename file names - replacing underscores with spaces - in a shell command line script



Question:

I am trying to rename all files in a folder replacing underscores with spaces.

i.e. this_is_a_test --> this is a test  

but somehow I'm messing up the quoting

> for file in * ; do echo mv -v $file $(echo $file | sed 's/_/\\ /g') ; done  mv -v this_is_a_test this\ is\ a\ test  

that looks OK, but if I remove the 'echo' mv complains as if the backslashes were removed

> for file in * ; do mv -v $file $(echo $file | sed 's/_/\\ /g') ; done  mv: target ‘test’ is not a directory  

Can someone point out the error of my ways?


Solution:1

There is a minor mistake. Use "$newfile" instead of only $newfile. You need to use "

Here is the correct one.

for file in * ; do mv -v "$file" "$(echo $file | sed 's/_/\\ /g')" ; done  

If you have filename this_is_a_test it will rename file to this\ is\ a\ test.

In case if you want to rename the file to this is a test. Use the code below,

for file in * ; do mv -v "$file" "$(echo $file | sed 's/_/ /g')" ; done  

It is a good practise to use variables inside "" while you writing good shell script.


Solution:2

Using rename:

rename -n 's/_/ /g' *  

If everything is ok, remove the -n switch:

rename 's/_/ /g' *  
~/tmp$ tree  .  â"œâ"€â"€ file  â"œâ"€â"€ file_1  â"œâ"€â"€ file_2  â"œâ"€â"€ file_3  â""â"€â"€ file_with_underscores    0 directories, 5 files  ~/tmp$ rename 's/_/ /g' *  ~/tmp$ tree  .  â"œâ"€â"€ file  â"œâ"€â"€ file 1  â"œâ"€â"€ file 2  â"œâ"€â"€ file 3  â""â"€â"€ file with underscores    0 directories, 5 files  


Solution:3

Instead of mv -v "$file" "$(echo $file | sed 's/_/\\ /g')" you can use a much simpler command mv "$f" "${f//_/ }".

To add it in your script:

for f in * ; do mv "$f" "${f//_/ }" ; done  

And that's it.


Solution:4

Use the find and rename command:

find <your_start_folder> -type f -regex ".*_+.*" -exec rename 's/_/ /g' {}  \;  

This command renames all file with a _ in the filename recursively.

Explanation

  • -regex ".*_+.*"

    Find all files with at least one _ in the filename

  • _

    Replace all occurrences of _ …

  • … with a space character ()


Example

% ls -Rog  .:  total 4  drwxrwxr-x 2 4096 Jun 15 17:39 foo  -rw-rw-r-- 1    0 Jun 15 17:34 foo_bar    ./foo:  total 0  -rw-rw-r-- 1 0 Jun 15 17:32 foo_bar    % find . -type f -regex ".*_+.*" -exec rename 's/_/ /g' {} \;    % ls -Rog                                                                   .:  total 4  drwxrwxr-x 2 4096 Jun 15 17:40 foo  -rw-rw-r-- 1    0 Jun 15 17:34 foo bar    ./foo:  total 0  -rw-rw-r-- 1 0 Jun 15 17:32 foo bar  


Solution:5

Here's an awk + for loop version:

for filename in *; do NEWNAME=$(echo "$filename" | awk '{gsub("_"," "); print}'); mv "$filename" "$NEWNAME";done

And here it is in action

$ ls                                                                                                                                                      $ echo "TEST" | tee {foo,bar}_{yolo,swag}_{whatever,else}.txt                                                                                           TEST    $ ls  bar_swag_else.txt      bar_yolo_else.txt      foo_swag_else.txt      foo_yolo_else.txt  bar_swag_whatever.txt  bar_yolo_whatever.txt  foo_swag_whatever.txt  foo_yolo_whatever.txt    $ for filename in *; do NEWNAME=$(echo "$filename" | awk '{gsub("_"," "); print}'); mv "$filename" "$NEWNAME";done                                        $ ls  bar swag else.txt      bar yolo else.txt      foo swag else.txt      foo yolo else.txt  bar swag whatever.txt  bar yolo whatever.txt  foo swag whatever.txt  foo yolo whatever.txt  

To account for newlines and possible renaming of directories, here's the typical IFS + find + read + while loop construct. Once suggestion through, run first the version with find . -type d -print0, because if you start renaming files first, and a subdirectory contains underscore, the filename won't be changed. In short, take care of directories and subdirectories first, then take care of the files

$ ls  file_bar_swag.txt  file_bar_yolo.txt  file_test_swag.txt  file_test_yolo.txt  foo_bar_swag.txt  foo_bar_yolo.txt  foo_test_swag.txt  foo_test_yolo.txt  tester_dir    $ ls tester_dir/                                                                                                                                                                                           ber_sweg_elze.txt  ber_sweg_whut.txt  ber_yelo_elze.txt  ber_yelo_whut.txt  fee_sweg_elze.txt  fee_sweg_whut.txt  fee_yelo_elze.txt  fee_yelo_whut.txt    $  find . -type d -print0 | while IFS="" read -r -d "" filename ; do NEWNAME=$(echo "$filename" | awk '{gsub("_"," "); print}'); mv "$filename" "$NEWNAME";done   mv: ‘.’ and ‘./.’ are the same file    $ ls  file_bar_swag.txt  file_bar_yolo.txt  file_test_swag.txt  file_test_yolo.txt  foo_bar_swag.txt  foo_bar_yolo.txt  foo_test_swag.txt  foo_test_yolo.txt  tester dir    $  find . -type f -print0 | while IFS="" read -r -d "" filename ; do NEWNAME=$(echo "$filename" | awk '{gsub("_"," "); print}'); mv "$filename" "$NEWNAME";done                                            mv: ‘./.yolo’ and ‘./.yolo’ are the same file    $ ls  file bar swag.txt  file bar yolo.txt  file test swag.txt  file test yolo.txt  foo bar swag.txt  foo bar yolo.txt  foo test swag.txt  foo test yolo.txt  tester dir  

As you can see, there's a small problem - because the find command lists current directory as well ( symbolized with a .), the command will create a byproduct - a file with a leading dot.

Adding ! -path . when you deal with directories solves the problem

$ find . ! -path .  -type d -print0 | while IFS="" read -r -d "" filename ; do NEWNAME=$(echo "$filename" | awk '{gsub("_"," "); print}'); mv "$filename" "$NEWNAME";done                                    $ find .  -type f -print0 | while IFS="" read -r -d "" filename ; do NEWNAME=$(echo "$filename" | awk '{gsub("_"," "); print}'); mv "$filename" "$NEWNAME";done                                              $ ls  file bar swag.txt  file bar yolo.txt  file baz swag.txt  file baz yolo.txt  foo bar swag.txt  foo bar yolo.txt  foo baz swag.txt  foo baz yolo.txt  tester dir    $ ls "tester dir"  file bar.txt  file baz.txt  foo bar.txt  foo baz.txt    $ ls -a  .  ..  file bar swag.txt  file bar yolo.txt  file baz swag.txt  file baz yolo.txt  foo bar swag.txt  foo bar yolo.txt  foo baz swag.txt  foo baz yolo.txt  tester dir  

Note:If u also have question or solution just comment us below or mail us on toontricks1994@gmail.com
Previous
Next Post »