How to split a string by ':' character in bash/zsh?
The :
there is any arbitrary character:
You can use:
parts=(${(s/:/)str})
Some common character pairs are also supported like:
parts=(${(s[:])str})
If you're going to use the @
flag to preserve empty elements, then you need to
quote:
parts=("${(@s[:])str}")
Otherwise @
makes no difference.
If it's to process variables like $PATH
/$LD_LIBRARY_PATH
... see also typeset -T
which ties an array variable to a scalar variable:
$ typeset -T str str_array
$ str='a::b'
$ typeset -p str
typeset -T str str_array=( a '' b )
zsh
does tie $path
to $PATH
by default (like in csh
/tcsh
).
bash's
parts=(${str//:/ })
Is wrong as it applies split+glob after having replaced :
with SPC.
You'd want:
IFS=: # split on : instead of default SPC TAB NL
set -o noglob # disable glob
parts=( $str"" ) # split+glob (leave expansion unquoted), preserve trailing
# empty part.
That code would also work in zsh
, if it was in sh
or ksh
emulation mode. If your goal is to write code compatible to both bash
and zsh
, you may want to write it using ksh syntax and make sure that zsh
is put in ksh
emulation (possibly only locally to some function) when interpreting it.
To test whether the shell is bash
or zsh
, you'd test for the presence of the $BASH_VERSION
/$BASH_VERSINFO
or $ZSH_VERSION
variables.
split() { # args: string delimiter result_var
if
[ -n "$ZSH_VERSION" ] &&
autoload is-at-least &&
is-at-least 5.0.8 # for ps:$var:
then
eval $3'=("${(@ps:$2:)1}")'
elif
[ "$BASH_VERSINFO" -gt 4 ] || {
[ "$BASH_VERSINFO" -eq 4 ] && [ "${BASH_VERSINFO[1]}" -ge 4 ]
# 4.4+ required for "local -"
}
then
local - IFS="$2"
set -o noglob
eval "$3"'=( $1"" )'
else
echo >&2 "Your shell is not supported"
exit 1
fi
}
split "$str" : parts
Related videos on Youtube
Adrian Maire
Enthusiast for most technological and scientific topics, specially computer science. I made a 300ECTS degree in computer science at UCLM (Spain) and I am currently working at Nexthink in Lausanne. I programmed my first ("finished") 2D video-game in VisualBasic-6 when I was 14, and since then, I spent most of my free time in personal projects: Real time graphics(opengl), AI neural networks, electronic, mechanical devices, house improvement...
Updated on September 18, 2022Comments
-
Adrian Maire over 1 year
I am searching for a way to split a string (variable) by
:
character, in a compatible way between bash and zsh.From different sources, I found the following command:
str="part1=part2=part3" parts=(${(@s:=:)str}) echo ${#parts[@]}
However, I could not find the way to escape
:
for this same sequenceparts=(${(@s:::)str}) #Not working parts=(${(@s:\::)str}) #Not working
For bash, I found this:
parts=(${str//:/ })
Which works, but is not really compatible. I could use the following line to discriminate the shell:
if [ -z "$(ps -p $$| grep zsh)" ]; then echo "This is bash (Use bash solution here)" else echo "This is zsh (Use Zsh solution here)" fi
But maybe some alternative solution is compatible? Any working solution is already a win.
-
Kusalananda over 3 yearsHow come that you need to do it the same way in both shells? Don't you know what interpreter you're using?
-
Adrian Maire over 3 yearsI need to do a script which is compatible with both shells, because I know both will be used.
-
Kusalananda over 3 yearsHuh, that's like asking for doing it the same way in Perl and Python, because the script will be executed by both. It doesn't make sense.
-
JoL over 3 years@Kusalananda There are projects like
rbenv
andrvm
that present themselves to the user as shell functions, in rbenv's case to be able to tie a ruby environment to an existing shell session. Given that bash and zsh are probably the most common shells in use by a wide margin and that their syntax are very compatible between each other (a lot more than Perl's and Python's), I think it makes a lot of sense for projects like that to try to be compatible with both. -
JoL over 3 years@Kusalananda There are also projects that add some shell initialization like how Gnome's VTE adds a file in /etc/profile.d/ compatible with both bash and zsh to set the window title to include the working directory of the shell as it changes.
-