【问题标题】:ZSH 5.05 string split on nullZSH 5.05 字符串拆分为空
【发布时间】:2014-07-17 03:01:46
【问题描述】:

正如this SO question 所指出的,ZSH 在默认情况下会挤压字符串拆分中的相邻分隔符。现在,在 ZSH 5.05 中,提供的修复不起作用。

Hexagon% string="1::3"
Hexagon% setopt interactive_comments
Hexagon% a=("${(s/:/)string}") # notice the quotes
Hexagon% 
Hexagon% echo $a[1] # 1, good
1
Hexagon% echo $a[2] # nothing, good
3
Hexagon% echo $a[3] # 3, good

如您所见,我的结果与之前 ZSH 上记录的结果不符。我怎样才能模拟这个功能,更好的是,有没有一种便携的方法可以做到这一点?

【问题讨论】:

    标签: python bash split zsh


    【解决方案1】:

    zsh 中尝试以下操作:

    IFS=: read -r -A a <<<"$string"
    
    • -A 将输入读入一个数组,在本例中名为a
    • IFS=:(本地化到命令)将: 定义为将输入拆分为字段的分隔符 - 多个相邻的分隔符被视为包含 empty 字段,因此 $a[2] 最终为空。

    这样做便携变得很棘手:

    这是一个适用于 zshbashksh 的解决方案 - 但是它不符合 POSIX(见下文):

    # Determine the shell-specific option character for `read`;
    # [-]A for ksh and zsh, [-]a for bash:
    readIntoArrayOptChar='A' && [[ -n $BASH_VERSION ]] && readIntoArrayOptChar='a'
    
    # Read the input string into array `a`, splitting into elements by ':'
    IFS=: read -r -$readIntoArrayOptChar a <<<"$string"
    
    # Determine the shell-specific start and end indices for printing
    # the array elements in a loop:
    # bash and ksh are 0-based, zsh is 1-based (by default).
    startNdx=0 endNdx=${#a[@]}
    [[ -n $ZSH_VERSION ]] && (( ++startNdx, ++endNdx ))
    
    # Print array elements individually.
    # Note how the element references are enclosed in {...} for cross-shell 
    # compatibility.
    for (( i = startNdx; i < endNdx; i++ )); do
      echo "el. $i: ["${a[i]}"]" # -> e.g., in zsh, 1st iteration: 'el. 1: [1]'
    done
    

    注意:

    • Python 的subprocess.call() 带有参数shell = Trueos.system() 默认调用sh,您可以只依赖POSIX 功能(并且,如果平台不是类Unix,甚至没有)。
    • 上述内容不符合 POSIX,主要是因为数组变量不是 POSIX shell 语言规范的一部分,正如 @rici 指出的那样。
    • @rici 还指出 bash/ksh 构造的 zsh 明显缺失 ${!a[@]} 用于获取数组的索引列表 (@987654338 @)。

    【讨论】:

    • @PythonNut: 是的,read 是所有 3 个 shell(bashkshzsh)中的内置,所以没有附加 进程已启动,但当然,shell 本身就是一个进程。你是如何调用shell的?请注意,Python 模块 subprocess 带有参数 shell = True,模块 os 带有 system() 默认调用 sh,您只能依赖 POSIX 功能(并且,如果平台不是类 Unix,甚至不是)。
    • @mklement0: printf %s\\n "${a[@]}" 适用于{ba,k,z}sh,但包含元素索引并不容易。 paste &lt;(printf %s\\n "${!a[@]}") &lt;(printf %s\\n "${a[@]}") 仅适用于 {ba,k}shzsh "${(k)a[@]}" 仅在 a 具有关联性时才有效。但我看不出使用(s/:/) 标志进行拆分有什么问题。
    • @rici:感谢您指出获取数组索引的差异。我的回答通过根据 shell 调整基本索引来演示显式数字索引的使用。这种差异可以通过zsh 中的emulate sh 解决(显然,还有其他副作用)——但是,这并不能解决"${!a[@]}" 问题。再说一次,我什至不清楚可移植性要求是什么。如果需要严格遵守 POSIX,我的答案无论如何都行不通。
    • @mklement0:“数组”一词排除了严格的 POSIX 合规性。我读到这个问题是关于zsh,因为这个词在问题中出现了四次,包括标题。 (我不知道为什么会有bash 标签,虽然否则我不会看到这个问题。)当然,比较其他shell 中的解决方案总是很有趣。
    • @rici:谢谢;奇怪的是,(k) 返回 valuesordinary 数组。 cat -n 便于为从 1 开始的 连续 数组生成索引列表,但不适用于 sparse 数组。事实上,我什至无法手动复制${!a[@]} 的行为(对于 bash/ksh 中的稀疏数组,它只返回 defined 元素的索引):而@987654364 @ 至少允许您通过 $#a 找到 maximum 索引(注意这与 bash/ksh 有何不同),它没有实现真正的稀疏数组:all 元素是隐式定义的,所以像[[ -z ${a[i]+defined} ]] 这样的测试不起作用。
    【解决方案2】:

    在 zsh 版本 5.0.2(可能是更高版本)中,您可以通过添加 @ 标志在拆分中保留空字符串:

    a=("${(@s/:/)string}")
    

    【讨论】:

    • +1;很高兴知道 - 绝对是最好的 zsh 解决方案(虽然不是便携式的)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-30
    • 1970-01-01
    • 2014-06-29
    • 2020-12-01
    • 2021-07-17
    相关资源
    最近更新 更多