I have a directory of files,

Measurements Panama 2009-03-22 Session1.xml
Measurements Panama 2009-03-22 Session2.xml
Measurements Panama 2009-03-22 Session3.xml
Measurements Panama 2009-03-22 Session4.xml
Measurements Panama 2009-03-22 Session10.xml

and I want to remove the blank spaces and the - dash characters. I don't want to use the rename command because I don't know perl or regular expressions. From another post a recommendation for removing part of a file name was:

for f in *.png; do mv $f ${f#image}; done

this looks nice. I can't figure out what the # character does (I assume it removes the string "image" from the file names?), anyways -- how do I remove the spaces and dashes?

I get an error that the files are not a directory when doing

[jings]s0128943: for f in *.xml; do mv $f ${f//[ -]/};done

mv: target `MeasurementsPanama20090322Session10.xml' is not a directory

mv: target `MeasurementsPanama20090322Session1.xml' is not a directory

etc for all the files.

  • 1
    Why do you think there will be an easy way without perl or regex? – Falmarri Oct 19 '10 at 17:58
  • I don't know, maybe im wrong. Why doesn't the attempted script work?? plz... – Vass Oct 19 '10 at 18:21

You can find information the ${...} substitutions in your shell's man page, for example bash(1). The most common:

  • ${var#word} remove shortest prefix: the value of variable var with the word prefix (if any) removed;

  • ${var%word} remove shortest suffix: the value of variable var with the word suffix removed (if any).

  • ${var/pattern/replacement} remove first occurrence of pattern

  • ${var//pattern/replacment} remove all occurrences of pattern

So, in your example ${f#image} expands to the value of f (e.g., image01.png) removing the image prefix, so it yields the value 01.png.

The word and replacement part in the ${...} expansions are subject to the same wildcard expansions as filenames; therefore, if you want to remove spaces and -, you could use ${f//[ -]/} (replaces any occurrence of characters and - with a null string.

All details on the man page.

  • I get an error that the files are not a directory[jings]s0128943: for f in *.xml; do mv $f ${f//[ -]/};done mv: target MeasurementsPanama20090322Session10.xml' is not a directory mv: target MeasurementsPanama20090322Session1.xml' is not a directory – Vass Oct 19 '10 at 16:32
  • 2
    @Vass Since your file names contain spaces you need to quote them, otherwise the shell will expand $f to separate words Measurements, Panama, etc. and thus make mv think that you want to move files Measuremens, Panama, etc. into a singleMeasurementsPanama... which must then be a directory. Use for f in *.xml; do mv "$f" "${f//[ -]/}"; done instead. (See Gilles' answer as well.) – Riccardo Murri Oct 19 '10 at 21:37

If you have the perl rename (e.g. because you're on Debian or Ubuntu), it is the simplest way of skinning this particular cat (as in an individual cat — there are plenty of tools for mass renaming, and searching the archives of this site and Super User should find all the major ones).

rename 's/[- ]//g' *.xml

That being said, your script would have worked if you had followed the most important shell programming principle: all variable substitutions must be double-quoted. (Why do you need this extra bit of syntax fluff? Because there are cases where you actually want the unquoted behavior. But these cases are rather rare.)

for f in *.xml; do mv "$f" "${f//[ -]/}"; done

With zsh, you wouldn't need to write a loop: you could use the convenient zmv function.

autoload zmv
zmv '(*.xml)' '${1//[- ]/}'
  • thanks alot. perl is a bit beyond me as well as zsh, but the other part is great. – Vass Oct 19 '10 at 22:57

You might also use sed to build the new name. There it would be

for i in *.xml; do mv "$i" `echo "$i" | sed -e "s/[ -]//g"`; done
  • 1
    Backticks are deprecated. Recommendet is $(...) instead, which is easy to nest, and not so easy to be overseen or confused with apostrophs: for i in *.xml; do mv "$i" $(echo "$i" | sed -e "s/[ -]//g"); done – user unknown Feb 10 '11 at 9:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.