6

For all files in a directory, I want to replace the underscores in the filename with spaces.

I tried this solution, which does the opposite of what I want: https://stackoverflow.com/questions/1806868/linux-replacing-spaces-in-the-file-names

But switched the space with the underscore. That does not work, giving the error

´x´ is not a directory

Where x is the last word in the filename, for example hello_world_x

What is the correct command to replace underscores with spaces for all files in a directory?

Anonymous Entity
  • 345
  • 2
  • 4
  • 7
  • 2
    It is a terrible ideia to have spaces in filenames, I would prefer a lot _, even for scripting. – Rui F Ribeiro Dec 02 '15 at 18:27
  • 1
    I deleted the comments but he really did have a point. Please don't do this. File names with spaces are a very bad idea and will make anything you need to do down the line more complex for no reason. I strongly urge you to reconsider and avoid using spaces. – terdon Dec 24 '15 at 10:20
  • And, this should still be linked here: http://unix.stackexchange.com/q/131766/135943 (@terdon: Thanks for the good moderation. :) – Wildcard Dec 30 '15 at 07:45

3 Answers3

12

Plagiarizing the code from the answer you linked to and making it more robust:

for file in *; do mv "$file" "$(echo "$file" | tr '_' ' ')" ; done

Quoting ensures that each file name is passed to mv as a single token, rather than it being broken at space boundaries.

If you have access to the Perl-based rename utility, the following will work as well:

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

(Remove the -n switch after confirming that the preview corresponds to what you would like to do.)

dhag
  • 15,440
  • 4
  • 54
  • 65
  • 2
    This will execute a move (`mv`) for **all** files whether they have `_` or not. Calling an external utility (tr) for something bash could do on its own is also not such a nice idea. –  Dec 02 '15 at 18:59
  • @BinaryZebra: Agreed. I made no effort to pass judgment on whether the request or referenced code are sane. – dhag Dec 02 '15 at 19:30
12

After you cd to the correct directory, this script will reliably solve your need (not portable because of the ${var//pat/str} expansion):

#!/bin/bash

set -- *_*
for file; do
    mv -- "$file" "${file//_/ }"
done

*_* The glob *_* will select all files that have an _ in their names.

set -- Those names (even including spaces or new-lines) will be reliably set to the positional parameters $1, $2, etc. with the simple command set -- "list"

for file; Then, each positional parameter will be (in turn) assigned to the var file.

do ... done contains the commands to execute (for each $file).

mv -- "$file" "${file//_/ }" will move (rename) each file to the same name with each (all) _ replaced by (space).

Note: You may add the -i (interactive) option to avoid overwriting already existing files. If the file exist, mv will ask. With a caveat: there needs to be an interactive shell where mv could communicate with the user.

mv -i -- "$file" "${file//_/ }"
  • 1
    @Wildcard Probably a better answer now. Changed to `-i`. –  Dec 21 '15 at 08:48
  • Great answer; I'd never used the `bash` variable substitution feature. One note about `mv`: when the target name exists already as a *directory*, `mv` will simply put the file *in* that directory without renaming either the file or the directory. Neither `-i` nor `-n` will prevent this behavior; you would have to check for the existence of the target first. (For the question asked about, this is an *extreme* edge case and more trouble than it's worth to check for, but for production environments it's good to be aware of.) If this is unclear: `touch file1; mkdir file2; mv -i -n file1 file2` – Wildcard Dec 21 '15 at 09:18
  • 1
    Yes @Wildcard the file will be moved, but, as no file was erased, no harm done. Having a directory with the (almost) same name of a file is just calling for trouble. –  Dec 21 '15 at 09:27
1
find . -depth -name "*_*" -exec \
    sh -cf '
        for f do   IFS=_
            IFS=\  set "${f%/*}/"${f##*/}$0
            mv "$f" "$*"
        done
    ' _ {} +

a POSIX sh will expand away the changes as configured with its Internal Field Separator.

mikeserv
  • 57,448
  • 9
  • 113
  • 229