There are a ton of different ways to split up a string in Zsh. This gist attempts to show them with examples to help you build your own. I write Zsh scripts all the time, and still reference back to this gist, so there you go.
From the Zsh docs on Parameter Expansion Flags (yeah - I know... how would anyone ever find that if they didn't know where to look!?)
j:string: Join the words of arrays together using string as a separator.
s:string: Force field splitting at the separator string.
You can also read more by running man zshexpn
. (Again, I know, right!? How would anyone know to look there!?)
Example splitting a string on slash character in Zsh.
$ str=part1/part2/part3
$ parts=(${(@s:/:)str})
$ echo $parts
part1 part2 part3
$ echo ${#parts[@]}
3
You can use split ${(@s:/:)str}
and indexing [start,end]
to do more sophisticated surgery on string parts. Note that Zsh array indexing starts a 1, not 0.
If you need to reassemble, simply join back on the same separator ${(@j:/:)str}
. Note: weirdly, the colons can be swapped out for other symbols, so if you prefer periods for example, this would also work: ${(@j./.)str}
. Since I'm splitting on slashes, I choose not to use slash as my symbol.
$ url="https://github.com/sorin-ionescu/prezto/blob/master/modules/history/init.zsh"
$ repo="${(@j:/:)${(@s:/:)url}[4,5]}"
$ echo $repo
sorin-ionescu/prezto
An example from the Zsh docs which shows splitting. Note the use of slash below unlike the colon above to surround the split character - remember that symbol is swapable and doesn't change the behavior of the split at all:
$ foo=(ax1 bx1)
$ print -l -- ${(s/x/)foo}
a
1 b
1
Getting the first 2 parts
$ str=a/b/c/d/e/f
$ parts=(${(@s:/:)str})
$ echo ${(@j:/:)parts[1,2]}
a/b
You can also use #
and %
parameter expansion symbols: see docs. #
removes from the left side, and %
from the right, which I remember by the fact that #
is to the left of %
on your actual keyboard. ##
and %%
use the longest match, while #
and %
use the shortest.
$ str=part1/part2/part3
$ echo ${str%%/*}
part1
$ echo ${str%/*}
part1/part2
$ echo ${${str%/*}#*/}
part2
$ echo ${str#*/}
part2/part3
$ echo ${str##*/}
part3
Word splitting is done with the '=' character:
$ sentence="ls -l -A -h"
$ arr=(${=sentence})
$ print -l -- $arr
ls
-l
-A
-h
I eventually arrived here because I originally found bash-specific suggestions that did not work in zsh, for example:
…the latter being available in bash starting with version 4 or later only.
This was what I was looking for, thanks 🙂 (I almost missed this as it was listed last 😬)
It's comparable to the ZSH equivalent of the first bash solution above:
It might be worth mentioning that (in both cases) the delimiter(s) used to split the words are controlled by the IFS environment variable? This may be a simpler solution for this case:
…than:
Also, I was ideally looking for a solution that works in both bash and zsh, and settled for this:
Using
printf
rather thanecho
because:zsh'secho
builtin processes escape sequences unless given the-R
flagecho
builtin does not support the-R
flagecho -e
would probably work the same in both zsh and bash?Edit: I misread the zsh builtin documentation, it's
print
that accepts-R
, notecho
. And both bash's and zsh'secho
look like they will accept-E
so a similar solution could be:…with a choice between
printf
/echo -e
vs.echo -E
depending on whether you want to interpret escape sequences or not.