cd foo bar
Is a feature copied from the Korn shell that chdir()
s to a path that is computed from the current working directory ($PWD
) with the first occurrence of foo
replaced with bar
.
That's all there is. If you want any other type of transformation, you need to do it by hand.
For instance, to cd
to $PWD
with all occurrences of foo
replaced with bar
, either use the csh-style¹:
cd $PWD:gs/foo/bar
Or the Korn-style²:
cd ${PWD//foo/bar}
(note that contrary to cd foo bar
, those don't print the path of the new working directory).
For your ~/foobar/foo/foo/www
to ~/foobar/bar/bar/www
, assuming $HOME
doesn't contain $HOME
doesn't contain /foo
:
cd $PWD:gs:foo/:bar/
See also:
cd $PWD:fs:/foo/:/bar/
There's a subtle difference between the f
and g
repeating modifiers:
g
acts like the g
flag of sed
's s
command: it replaces all occurrences, looking for all those occurrences one after the other in one pass in the string
- while
fs:string:replacement
repeats the s:string:replacement
from the start as long as it does any modification. Note that while g
is limited to working with :s/string/replacement
, f
can be used with any modifier; for instance $file:fr
is like $file:r
(get the r
oot name, IOW remove the extension) except it removes all extensions.
gs:/foo/:/bar/
like sed s:/foo/:/var/:g
would change ~/foobar/foo/foo/www
into ~/foobar/bar/foo/www
only, because after the first /foo/
has been replaced with /bar/
, s
carries on searching for other occurrences in what's left but that's foo/www
which doesn't contain any /foo/
.
While fs:/foo/:/bar/
is more like sed -e :1 -e s/foo/bar/ -e t1
³ and works because s
is called first on /home/you/foobar/foo/foo/www
and then on /home/you/foobar/bar/foo/www
.
Also note that in $var:s/foo/bar
, foo
is taken as a fixed string (unless the histsubstpattern
option is set) while in ${var/foo/bar}
, it's taken as a pattern. The next version of zsh
will have $var:S/foo/bar
where foo
is taken as a pattern regardless of the setting of the histsubstpattern
option.
Another approach to replace all path components that are foo
as a whole (including the last that the above approach doesn't address) would be to
- split on
/
- replace all matching elements in the resulting array.
- join back with
/
cd ${(j[/])${(s[/])PWD}:/foo/bar}
Here using zsh's ${var:/pattern/replacement}
extension over ksh's ${var/pattern/replacement}
for the pattern to match the whole string only.
Or, with histsubstpattern
(or :S
in the next version), use the W[/]
modifier for the s
modifier to act on each /
-separated W
ord:
set -o histsubstpattern # typically in ~/.zshrc
cd $PWD:W[/]s/#%foo/bar
(where #
anchors the pattern at the start, and %
at the end4).
¹ Well, technically, the :s/string/replacement
is from csh
, but g
is a zsh extension, similar to tcsh's a
. $PWD
is from ksh, though tcsh has $cwd
as an equivalent. Of course, in (t)csh, as usual, you'd also need a :q
to "quote" the result.
² Well, in ksh, as usual, you'd also need to quote the expansion to prevent split+glob
³ that's not strictly equivalent as sed
's t1
would repeat (branch to the :1
label) if any substitution was done while zsh's :fs/pattern/replacement/
repeats if a substitution was done and resulted in a change. For instance with s/foo/foo/
, sed
would run in an infinite loop, while zsh
's f
would stop at once.
4 like in ksh's ${var/pattern/replacement}
except that ksh can't combine the two.