69

How do you rename all files/subdirs in the current folder?

Lets say, I have many files and subdirs that are with spaces and I want to replace all the spaces with an underscore.

File 1
File 2
File 3
Dir 1
Dir 3

should be renamed to

File_1
File_2
File_3
Dir_1
Dir_3
NobbZ
  • 807
  • 1
  • 8
  • 8

8 Answers8

64

In any shell, you can loop over the files whose name contains a space. Replacing the spaces with underscores is easy in bash, ksh and zsh with the ${VARIABLE//PATTERN/REPLACEMENT} construct.

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

On Debian, Ubuntu and derivatives, you can use the Perl rename (other distributions ship a different program as rename, and that program isn't helpful here).

rename 's/ /_/g' ./*

An obligatory zsh solution:

autoload zmv
zmv '(*)' '${1// /_}'

Or:

autoload zmv
zmv '*' '${f// /_}'

An obligatory POSIX solution:

for x in *" "*; do
  y=$(printf %s/ "$x" | tr " " "_")
  mv -- "$x" "${y%/}"
done
Stéphane Chazelas
  • 522,931
  • 91
  • 1,010
  • 1,501
Gilles 'SO- stop being evil'
  • 807,993
  • 194
  • 1,674
  • 2,175
  • What does the 'g' at the end of the "rename" command mean? I didn't see it in the manual. – JulianLai Jan 15 '19 at 08:40
  • How to rename directory only? I don't want to change the filename. – JulianLai Jan 15 '19 at 08:44
  • @JulianLai `s/…/…/g` means to replace all occurrences. It's not very well explained in [the manual](https://perldoc.perl.org/perlop.html#Regexp-Quote-Like-Operators). If you want to rename directories, there are ways, please search for it (I think I've seen it before) and if you can't find it ask a new question. – Gilles 'SO- stop being evil' Jan 16 '19 at 23:11
  • great ... this helped me to rename all `_MG_blah.JPG` to `IMG_blah.JPG` . – Abinash Dash May 22 '19 at 08:25
  • 1
    What is `--` for in mv command? –  May 23 '20 at 15:47
  • 1
    @0x476f72616e So that if `$x` starts with a `-` it isn't interpreted as an option. – Gilles 'SO- stop being evil' May 23 '20 at 17:39
  • To recursively replace every space in every file name use: `find dirpath/ -depth -type f -exec rename 's/ /_/g' {} \;` where to do this to only directory names replace `-type f` with `-type d` while for BOTH file names and directory names, remove the `-type ` flag. If only replacing file names then `-depth` is optional. You may need to download the `rename` command: `sudo apt install rename`. Credit for solution: https://unix.stackexchange.com/a/282364/380315 – Matthew K. Mar 30 '21 at 21:39
  • @JulianLai the syntax of "s/.../.../" with optional g at the end, or perhaps other regex switches at the end, is commonly known in an old program called sed, which probably invented it. – barlop Sep 07 '22 at 02:53
49

If you need to rename files in subdirectories as well, and your find supports the -execdir predicate, then you can do

find /search/path -depth -name '* *' \
    -execdir bash -c 'mv -- "$1" "${1// /_}"' bash {} \;

Thank to @glenn jackman for suggesting -depth option for find and to make me think.

Note that on some systems (including GNU/Linux ones), find may fail to find files whose name contains spaces and also sequences of bytes that don't form valid characters (typical with media files with names with non-ASCII characters encoded in a charset different from the locale's). Setting the locale to C (as in LC_ALL=C find...) would address the problem.

Stéphane Chazelas
  • 522,931
  • 91
  • 1,010
  • 1,501
enzotib
  • 50,671
  • 14
  • 120
  • 105
  • You would need to use `find -maxdepth 1` to do exactly what the OP asked about operating on the current folder. – Caleb Aug 20 '11 at 06:34
  • 1
    Use find's `-depth` option, and you can get rid of the `sort`. – glenn jackman Aug 20 '11 at 14:52
  • @glenn jackman: thank you, I now understand my solution was wrong. – enzotib Aug 20 '11 at 15:19
  • 1
    Could some one please elaborate on what should be change so this could be used to replace other characters. For example I could not make it work for replacing _ with a period (.) – kroiz Jan 24 '13 at 10:34
  • 3
    Change `-name '* *'` to `-name '*_*'` and change `"${1// /_}"` to `"${1//_/.}"` – enzotib Jan 24 '13 at 19:41
  • Thanks @enzotib, I want to google for help about this `"${1// /_}"`. What is it called? – kroiz Jan 26 '13 at 06:03
  • 1
    @kroiz: it is called "Pattern substitution". You could find it in `bash`'s man page. – enzotib Jan 30 '13 at 09:31
  • @enzotib: What's the purpose of the second bash command? – benba Jan 06 '20 at 22:35
  • 1
    @benba: it is not a second bash command, you should interpret it as: `bash -c script-string $0 $1 $2 etc`, so the second time the string `bash` appears, it is in the `$0` position, and so gives a name to the script. – enzotib Jan 07 '20 at 12:19
  • @enzotib: got it thanks – benba Jan 11 '20 at 17:25
20

You can use rename for this (here assuming the one from util-linux, not the perl one, and not the removed one):

cd /path/to/dir
rename ' ' _ *\ *

This will find all files and directories space in the name and replace the space with an underscore. Since it uses glob file matching you need to be in the right directory to start with.

If you want to do recursive matches you can, but you might have to execute the rename a couple times to catch any items in directories that themselves got renamed:

cd /path/to/dir
shopt -s globstar
rename ' ' _ **/*\ *
!!; !!
malat
  • 2,708
  • 4
  • 27
  • 47
Caleb
  • 69,278
  • 18
  • 196
  • 226
  • 4
    “`rename` will rename the specified files by replacing the **first** occurrence of *from* in their name by *to*.” So this will only work for files with a single space in their name. (You could call `rename` in a loop, but it's not really the right tool here.) – Gilles 'SO- stop being evil' Aug 21 '11 at 00:54
  • 3
    rename has a `-a` parameter that tells it to replace all occurrences – NeuroXc Jul 12 '22 at 17:04
  • That globstar approach won't work. You'd need to process the files depth-first (which bash contrary to zsh can't do) and transform the base names only. You're also missing some `--`s – Stéphane Chazelas Jun 26 '23 at 08:10
1

On Debian/Ubuntu, building upon the answers of Caleb and Gilles, this is what worked for me to rename files recursively:

cd /path/to/dir
shopt -s globstar
rename 's/ /_/g' **

Note: To preview what files would be renamed and how, use the -n switch with rename:

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

Another note: setting globstar makes ** match files in all subdirectories, so if only current directory is desired, don't set globstar or use * instead of **.

One more note: The rename command needs to be run more than once for files with multiple occurrences of the search term.

  • That only works if directories don't contain spaces. (a rename of `a b/c d` to `a_b/c_d` wouldn't work, you'd need first to rename `a b/c d` to `a b/c_d`, and then `a b` to `a_b`). – Stéphane Chazelas Dec 20 '13 at 16:15
  • @Stephane: Does it mean to run the same `rename` command twice? – Markus Pscheidt Dec 20 '13 at 16:19
  • Well, more like as many times as there are nested levels of directories with spaces. Ideally, you want to traverse the directory depth first, and convert only the basename of the file like in the accepted solution. Also note that bash's `**` excludes dotfiles and traverses symlinks. – Stéphane Chazelas Dec 20 '13 at 16:26
1

Another option would be mmv, if installed.

mmv \*\ \* \#1_#2
glglgl
  • 1,200
  • 9
  • 12
0

If you're not a flash at regular expressions (I'm not!), and you can run applications designed for kde (either you use kde - k desktop or you have it's libraries installed), then krename is a great graphical utility that lets you see the before and after before you commit to the changes. It has a number of simple transformations as options and also supports regular expressions. You can even combine several sequential transformations into one rename so you don't have to design a single complex transform that does it all at once. It also has an option to continue renaming the same files after a rename has been applied.

I don't use it that often, but when I do, it really gets the job done quickly and easily. It really comes in handy when renaming various downloaded media files so you can manage them uniformly on your system. It helps to download the krename manual separately so you can refer to it while using the program.

http://www.krename.net/

Joe
  • 1,368
  • 1
  • 16
  • 18
0

Suppose you have only the traditional rename command installed (the one that comes with Slackware).

And you have those files:

"File 1 another space"
"File 2 with other space"
"Dir 1 also"
"Dir 2 and so on"
  • If you issue:

     rename -v ' ' _ "File 1 another space"
    

you get:

"File_1 another space"
  • If you issue:

     F=$(ls -1 File_1*) ; while [[ "$F" =~ " " ]] ; do rename -v " " _ $F ; sleep 1 ; F=$(ls -1 File_1*) ; done
    

You will get

"File_1_another_space"
  • Then the complete solution would use two loops such as:

    for F in * ; do \
      while [[ "$F" =~ " " ]] ; do \
        G=$(rename -v " " _ "$F" | cut -d '`' -f3) ; \
        F="${G::-1}" ; \
        echo $F ; \
        sleep 1 ; \
      done ; 
    done
    

The traditional rename command only changes the first character found that match the pattern.

Because of the echo command above (optional, you may want to remove them, as well as the sleep), you will get the following output:

Dir_1 also
Dir_1_also
Dir_2 and so on
Dir_2_and so on
Dir_2_and_so on
Dir_2_and_so_on
File_1 another space
File_1_another space
File_1_another_space
File_2 with other space
File_2_with other space
File_2_with_other space
File_2_with_other_space
DrBeco
  • 754
  • 7
  • 21
  • Not sure why you'd call that dumb `rename` implementation from `util-linux` *traditional*. It is Linux-specific and was added to util-linux 2.10e circa 2000 over a decade after the `rename` from `perl` which was added in perl 3 from 1989 (predates even Linux). – Stéphane Chazelas Nov 25 '22 at 12:49
  • Call it whatever you like. Just remember to be a nice boy while you are at it. Peace. – DrBeco Nov 25 '22 at 22:19
-1
for f in *\ *; do mv "$f" "${f// /}"; done

(Given that you are in the directory where you want to do this operation).

truth
  • 101