5

I have a list of files that are using %20 to indicate a space in their names.

I'm currently trying to do the following to change them all from %20 to a space " ".

for x in *.txt
  do mv $x $(echo $x | sed -e 's/%20/ /')
done

That doesn't play nice. It spits out this:

usage: mv [-f | -i | -n] [-v] source target
       mv [-f | -i | -n] [-v] source ... directory

But I do the same thing on some of my images when I'm importing them and it works fine:

for x in *_MG*.CR2
  do mv $x $(echo $x | sed -e 's/_MG/_IMG/')
done

Forgetting that one is using .TXT and the other is .CR2, what am I totally overlooking here?

Keeping in mind I'm using a work laptop so I can't add Homebrew to install any additional software. And it's Mac OS X for the OS.

3 Answers3

10

Use rename and replace the %20 with a space in all type of files:

$ rename -n 's/%20/ /g' *

File%20with%20in00.yA2 renamed as File with in00.yA2
File%20with%20in01.h9H renamed as File with in01.h9H
File%20with%20in02.CNR renamed as File with in02.CNR
File%20with%20in03.PuP renamed as File with in03.PuP
File%20with%20in04.js8 renamed as File with in04.js8
File%20with%20in05.KdZ renamed as File with in05.KdZ

In OS X you can use a specialized tool such as the Perl-based rename utility. you can install it using popular package manager Homebrew : brew install rename

Note: remove -n option to perform actual renaming.

αғsнιη
  • 41,407
  • 5
    It might be good to mention that this only works on systems where the Perl rename utility is installed, i.e. Ubuntu. It may or may not work for all systems. IIRC, Mac OS X does not ship with the Perl rename utility by default, meaning it must be installed with Homebrew or similar. – Kaz Wolfe Dec 14 '14 at 10:39
  • I do not have access to be able to install any software on this mac. We only have general user access. Also, what does the /g part mean? – Danijel-James W Dec 14 '14 at 13:59
  • @DanijelJ Owkey. no problem. And g on the end means replace all matches with space, not just the first match. – αғsнιη Dec 14 '14 at 14:33
  • 1
    @DanijelJ rename is just a perl script, you can also install it manually. Get it from here (Example 9.5), paste it into a text file called rename and run chmod +x rename in Terminal. Please note that this version does not support -n. – nohillside Dec 14 '14 at 15:36
7

Since the output of the mv probably will have spaces you need to put double quotes around the result in order not to try and execute commands in the for loop like:

mv abc%20def abc def

where mv has too many arguments. These are the ones giving you the usage: message.

What you should do is:

for x in *_MG*.CR2
do 
  mv -- "$x" "$(printf '%s\n' "$x" | sed 's/_MG/_IMG/')"
done
Anthon
  • 79,293
  • the example using _MG to _IMG works perfectly fine. Those particular files mentioned in that example have no spaces in them. It was the replacing of %20 with " " that was the pain. – Danijel-James W Dec 14 '14 at 14:06
6

There's no need to use sed, this can be handled by parameter expansion

mv -- "$x" "${x//%20/ }"

FWIW, I'd be replacing those %20s with an underscore (or something); I hate file names that contain spaces. But I guess learning how to write bash scripts that can handle spaces and other special characters in file names is a Good Thing. :)

As Izkata mentions in the comments, it's very important to quote parameters! Double quotes in bash (& related shells) aren't mere string delimiters, they also signify that you don't want word-splitting to occur. So if x contains a filename with a space in it $x will be treated as two arguments, but "$x" will be treated as a single argument. If we use single quotes we inhibit parameter expansion so '$x' just results in a literal string containing $x, which is generally not what we want. :)

PS. Thanks to Monsieur Chazelas for supplying the missing -- in my command line. -- indicates the end of options in the argument list; without it it's possible for file names commencing with - to be interpreted as options.

PM 2Ring
  • 6,633
  • 1
    This was my thought as well, but it might be worth putting more emphasis on the reason for the OP's problem, which is the space, not the use of an unnecessary pipeline. – David Z Dec 14 '14 at 13:06
  • +1 for the two important editorial observations: avoid spaces in file names (stuff breaks...) and learn to handle them properly (so when stuff breaks it's not your fault, at least). – Floris Dec 14 '14 at 13:48
  • 1
    Can you explain to me how the "${x//%20/ }" part works. This was magic and exactly what I needed. – Danijel-James W Dec 14 '14 at 14:01
  • 1
    @DanijelJ, the "${x//%20/ }" part utilizes extended form of parameter expansion which is available in contemporary bash and other shells. Simple parameter expansion looks like ${parameter} and by adding various modificators after the parameter you can tell your shell to perform additional operations on parameter value. The ${parameter/pattern/string} form replaces a part of parameter that matches the pattern with the string, if pattern begins with / all matching parts are replaced. – Mr. Deathless Dec 14 '14 at 18:02
  • 1
    For anyone interested in possibilities of extended parameter substitution, I would suggest to read a relevant section at Greg's wiki. – Mr. Deathless Dec 14 '14 at 18:08
  • @PM 2Ring Can you please add the content of your comment in your answer? – A.L Dec 14 '14 at 19:41
  • @DanijelJ The key reason this works and the version in your question doesn't, that both PM2Ring and Mr.Deathless sidestepped, is that you have to quote the arguments so the new space doesn't split the second filename into multiple arguments, as described in Anthon's answer – Izkata Dec 14 '14 at 22:59
  • Thanks @StéphaneChazelas for the -- to stop file names commencing with - from being treated as options. I keep forgetting to do that... – PM 2Ring Dec 15 '14 at 03:25