29

I'm trying to rename a bunch of files which contain spaces in them, getting rid of the spaces. I thought I found the correct bash command:

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

However, this gives the error, "mv: target is not a directory" for each file. If I replace 'mv' with 'echo mv' in the command, it prints the proper mv command for each file, and if I type any of those mv commands individually, they work. For example, if I have 2 files, "a .txt", and "b .txt", and run the command above, I get:

mv: target 'a.txt' is not a directory
mv: target 'b.txt' is not a directory

If I type the command:

for f in *.txt; do echo mv \"$f\" ${f/ /}; done

I get:

mv "a .txt" a.txt
mv "b .txt" b.txt

I've found another way to do this, using "rename", but I would like to know why this doesn't work.

Jim Hines
  • 291
  • 1
  • 3
  • 3
  • I'm wondering how you put spaces before the .txt because it won't accept that. It will think .txt is not a directory – Moonhead Oct 23 '14 at 00:22

2 Answers2

43

Try:

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

Three points:

  1. The quotes around a shell variable should not be escaped.

  2. In general, it is a good idea to put double-quotes around every reference to a shell variable.

  3. ${f/ /} removes just the first occurrence of a space. To remove all spaces, use ${f// /}.

What went wrong

$ touch {a,b}" .txt"
$ ls *.txt
a .txt  b .txt
$ for f in *.txt; do mv \"$f\" ${f/ /}; done
mv: target `a.txt' is not a directory
mv: target `b.txt' is not a directory

The expression \"$f\" does not behave like it is double quoted. It expands to two arguments, such as "a and .txt", where the double-quotes are treated as normal characters, just like the a is a normal character. Because there are three arguments to mv ("a and .txt" and a.txt), mv believes that you are trying to move the first two arguments to the third and the third is required to be a directory. Since the third is not a directory, it issues an error message.

John1024
  • 109,961
  • 14
  • 137
  • 171
  • Is there a way to keep spaces? And still, rename the file? I'm getting mv: target is not a directory, but the full name of the file is "Jam Banner Transparent.png", I take the name add size and rename it, but still have this error. The files that have "_" instead of spaces are renamed as expected. I expect it to be "Jam Banner Transparent-300-400.png" – Candid Moon _Max_ Jun 01 '17 at 12:49
  • 2
    @CandidMoon If you are getting the "not a directory" message, that likely means that the shell variables weren't in double-quotes. Of course, without knowing what, precisely, you are doing, I can only guess. – John1024 Jun 01 '17 at 19:40
  • https://ideone.com/yQaViz this is my code, I use imagemagick and cowsay. I know that it would be better if I created a new question about it because of the points, though thanks for replying. – Candid Moon _Max_ Jun 01 '17 at 20:22
  • 1
    I see: `convert $fullfilename -resize ${new_width_height[0]}x${new_width_height[1]} $new_file_name` If the file names contain spaces, they need to be inside double-quotes. – John1024 Jun 01 '17 at 22:14
12

Because this is the first thing that came up on google when googling this error, I thought I'd add a bit.

This error occurs if you have more than two arguments and the last result is not a directory.

This works (when output.txt does not exist):

mv file1.txt output.txt

This does not (if file2.txt does exist):

mv file1.txt file2.txt

In my case I was doing: mv prefix_* output_file_name to ensure a downloaded file had a consistant name, but another file had appeared in the directory, restulting in the "mv target is not a directory" error

sdfgeoff
  • 854
  • 9
  • 16