如何在 bash/zsh 中用 ':' 字符分割字符串?

Adr*_*ire 4 shell bash zsh string

我正在寻找:一种在 bash 和 zsh 之间以兼容的方式按字符拆分字符串(变量)的方法。

从不同的来源,我发现了以下命令:

str="part1=part2=part3"
parts=(${(@s:=:)str})
echo ${#parts[@]}
Run Code Online (Sandbox Code Playgroud)

但是,我找不到:相同序列的逃生方法

parts=(${(@s:::)str})  #Not working
parts=(${(@s:\::)str}) #Not working
Run Code Online (Sandbox Code Playgroud)

对于 bash,我发现了这个:

parts=(${str//:/ })
Run Code Online (Sandbox Code Playgroud)

哪个有效,但并不真正兼容。我可以使用以下行来区分外壳:

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
Run Code Online (Sandbox Code Playgroud)

但也许一些替代解决方案是兼容的?任何有效的解决方案已经是一个胜利。

Sté*_*las 12

:有任意字符:

您可以使用:

parts=(${(s/:/)str})
Run Code Online (Sandbox Code Playgroud)

还支持一些常见的字符对,例如:

parts=(${(s[:])str})
Run Code Online (Sandbox Code Playgroud)

如果要使用@标志来保留空元素,则需要引用:

parts=("${(@s[:])str}")
Run Code Online (Sandbox Code Playgroud)

否则@没有区别。

如果是工艺变量,例如$PATH/ $LD_LIBRARY_PATH...又见typeset -T一个数组变量一个标量变量:

$ typeset -T str str_array
$ str='a::b'
$ typeset -p str
typeset -T str str_array=( a '' b )
Run Code Online (Sandbox Code Playgroud)

zsh确实领带$path$PATH默认(如在csh/ tcsh)。

bash的

parts=(${str//:/ })
Run Code Online (Sandbox Code Playgroud)

是错误的,因为它在替换:为 SPC后应用了 split+glob 。

你会想要:

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.
Run Code Online (Sandbox Code Playgroud)

zsh如果它处于shksh仿真模式,该代码也可以在. 如果您的目标是编写bash与 和兼容的代码zsh,您可能希望使用 ksh 语法编写它,并确保在解释它时将其zsh置于ksh仿真中(可能仅在某些函数的本地)。

要测试 shell 是否为bashzsh,您需要测试$BASH_VERSION/$BASH_VERSINFO$ZSH_VERSION变量是否存在。

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
Run Code Online (Sandbox Code Playgroud)