How it is possible, using zmv, to replace any number of spaces with a single underscore?

What I use currently is

zmv -- '* *' '$f:gs/ /_'

but it replaces each space, and so foo bar.txt become foo___bar.txt, not foo_bar.txt (which I would it to be).

1 Answer 1


What you want here is the equivalent of the regexp + operator which in zsh with extendedglob on (and zmv does enable extendedglob) is the ## glob operator.

The :s/.../.../ modifier from csh doesn't do pattern matching unless you set the histsubstpattern option. zmv doesn't set that option and resets the options to a sane default, so you can't use it there. The next version of zsh (whether that's 5.10 or 6.0) will have :S (actually on my request and for that very purpose) which does pattern matching unconditionally, so with those, you'll be able to do:

zmv '* *' '$f:gS/ ##/_'

But with any version of zsh, you can also use the ksh-style ${param//pattern/replacement} instead:

zmv '* *' '${f// ##/_}'

Note that -- is not necessary here as the first argument doesn't start with -.

  • Many thanks, Stéphane. I have noticed that you put -- even when it is not necessary (for consistency, I suppose), and I adopted your practice.
    – jsx97
    Commented May 17 at 10:14
  • "The next version of zsh will have :S" - Could you show how it will work with :S?
    – jsx97
    Commented May 17 at 10:15
  • 1
    @jsx97, see edit. Commented May 17 at 17:41
  • And to make it replace not only single spaces, but also space-hyphen-space (so that foo - bar become foo_bar, I should use zmv -- '* *' '${f//( #-## #| ##)/_}', correct?
    – jsx97
    Commented May 18 at 5:16
  • 1
    Yes, though as it also replaces the hyphens that are not surrounded by spaces, it would probably make more sense to do zmv '*[- ]*' '${f//( #-## #| ##)/_}' or just zmv '*' '${f//( #-## #| ##)/_}' Commented May 18 at 8:20

You must log in to answer this question.

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