39

I named a number of files with spaces in them, and I want to replace the space with _. However, every time I write a command in the shell with the file name (eg Spring 2011), the shell doesn't recognize the file or directory.

What can I do about this? Is there any way to use the unicode character for a space?

1

6 Answers 6

39

Escape the space, e.g. Spring\ 2011, or use quotes, e.g. 'Spring 2011'. In the future, it's typically a bad idea to use file names with spaces in them on any *NIX.

If you've got rename, you can use this:

rename ' ' '_' [filenames...]
11
  • 1
    Do you know is there anyway to do this for all the files at once?
    – Phil Braun
    Jun 8, 2011 at 19:55
  • 4
    Why is it a bad idea? It's the responsibility of the programmer to handle filenames properly. Jun 9, 2011 at 14:28
  • 1
    @rafe, really it's error prone not to handle filenames properly up front. The extra effort comes when having to debug a script when you simply forgot to quote variables containing a filename. Jun 9, 2011 at 15:52
  • 3
    This answer does not seem to work (on Ubuntu 12.04 LTS, at least). The answer from unutbu works fine though.
    – DNA
    Sep 3, 2012 at 14:44
  • 1
    this only did the first space for me... which is not helpful (i have rename version 2.23) Oct 5, 2018 at 14:13
39

If you don't have rename or prefer to use just the shell:

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

Broken down:

  • *\ * selects all files with a space in their name as input for the the for loop. The pattern *X* selects all files with X in their name, and for the special character space, we have to escape it with a slash so that bash doesn't treat it as separating different arguments.
  • The quotes around "$f" are important because we know there's a space in the filename and otherwise it would appear as 2+ arguments to mv.
  • ${f//str/new_str} is a bash-specific string substitution feature. All instances of str are replaced with new_str.
6
  • 2
    this works but for me it only replaces the first space. if i use for f in *\ *; do mv "$f" "${f// /_}"; done it works Jul 7, 2014 at 21:52
  • @billynoah Good catch, updated.
    – blahdiblah
    Jul 7, 2014 at 22:05
  • @blahdiblah Thank you so much! You just saved me so much time!
    – Guerrilla
    Feb 20, 2016 at 17:03
  • @blahdiblah your solution is way way more elegant than my solution. good job. I didn't think the for loop glob would work... so i did the glob outside of the for loop. Oct 3, 2018 at 17:58
  • That was so quick and easy, and the bash is semi-understandable. How does the *\ * work to select all files? Jan 29, 2021 at 21:15
36

If your machine has the rename command, then this will change all spaces to underscores in all files/dirs in the current working directory:

rename 's/ /_/g' *
4
  • 2
    Confirmed to work in Ubuntu 12.04 Oct 10, 2014 at 6:50
  • 1
    didn't work for me... i got an error of "rename: not enough arguments". i have version rename --version rename from util-linux 2.23.2. Oct 5, 2018 at 14:12
  • -bash: /usr/bin/rename: Argument list too long
    – nmz787
    Dec 8, 2019 at 10:04
  • There are two main rename utilities. "rename from util-linux" and "perl-rename". The one from util-linux doesn't support regular expressions. You may need to explicitly install the perl one to do regular expressions depending on your distro. Jul 29, 2020 at 15:27
6

mv "Spring 2011.file" Spring_2011.file should tell the command-line to take the quoted string as a single input.

0
2

To programmatically rename N files, you can use a simple bash for loop.

#!/bin/bash

set -eux

# find all your files by using a `grep` pattern
pattern="insert_filename_pattern_here" # you must enter your filename pattern here

# create an array of filenames and split on newlines
IFS=$'\n'
tmp=($(ls | grep ${pattern}))
unset IFS

# for each filename
for filename in "${tmp[@]}"; do
    # rename the filename to use "_" character instead of a " " character
    mv -v "${filename}" "${filename// /_}"
done

If you have less than 5 filenames, then you can manually type in all the filenames and use the rename command like the above solution suggests. But for me... I prefer the programmatic solution... even when there is only 4 files.

p.s.

if you aren't familiar with Bash's Parameter Expansion (i.e. ${filename// /_}) you could use the rename command:

if ((0)); then
    mv -v "${filename}" "${filename// /_}"
else
    rename ' ' '_' "${filename}"
fi
2

Use the command 'detox' it has useful options like dry run, recursive, can be called on a single file, directory, or an entire filesystem if you wish

1
  • Amazing function, very easy to use. Thanks!
    – Gildas
    Jul 9, 2021 at 11:37

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .