Replace any number of spaces with a single underscore


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).

Best Answer

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 -.

Related Question