V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
adoyle
V2EX  ›  Bash

请教 Shell 空字符串数组的问题

  •  
  •   adoyle · 2019-06-17 10:02:35 +08:00 · 7582 次点击
    这是一个创建于 1986 天前的主题,其中的信息可能已经有所发展或是发生改变。
    #!/usr/bin/env bash
    
    k=("$(printf '\n\n\n')")
    echo ${#k[@]} 
    k="$(printf '\n\n\n')"
    echo ${#k[@]}
    

    我发现 k 的长度都是 1,怎么样才能让它是 3 呢?

    还有个类似的问题,

    #!/usr/bin/env bash
    
    f() {
      local o=('' '' '')
      printf '%s\n' "${o[@]}"
    }
    
    k=$(f)
    echo ${#k[@]}
    

    我想通过函数 f 来返回一个长度为 3 的空字符串数组给 k。应该怎么改才能做到呢?

    9 条回复    2019-07-05 17:20:43 +08:00
    yangg
        1
    yangg  
       2019-06-17 11:30:55 +08:00
    k=$(printf \n\n\n)
    echo ${#k[@]}
    adoyle
        2
    adoyle  
    OP
       2019-06-17 13:20:17 +08:00
    @yangg 我在 bash 4.4 和 bash 5.0 都试过了,还是 1
    yangg
        3
    yangg  
       2019-06-17 17:14:06 +08:00
    @adoyle 那好吧,我在 zsh 试了下是 3 没想到 这个跟 bash 还有区别
    Kobayashi
        4
    Kobayashi  
       2019-06-18 13:41:34 +08:00
    @yangg 你这种做法根本没捕捉到空字符串。你可以输出 echo ${k[@]} 试试,输出的是 nnn

    @adoyle 因为你返回的是空字符串,所以需要特殊方法捕捉,直接赋值为数组是不行的。

    https://gist.github.com/laggardkernel/18422f9f21063da1a7ebabb96c004e3d

    另外,例 1 中,你的引号使用方法完全是错误的。建议了解一下 word-splitting,不要乱用引号。例 2 中,函数只能返回字符串。而我开头已经说过了,空字符串 "" 需要特殊照顾才能被解释到数组中的。

    最后送楼主一句话:明知山有虎,……
    adoyle
        5
    adoyle  
    OP
       2019-06-18 16:06:25 +08:00
    @Kobayashi 感谢回复。while + read 是个好办法,如果用 bash 4 的话,可以用 readarray 来简化写法 `readarray k < <(printf '\n\n\n')`。

    > 另外,例 1 中,你的引号使用方法完全是错误的。建议了解一下 word-splitting,不要乱用引号。

    恩,例 1 中的引号对于我的问题的确没有意义。不过乱用引号不会造成额外问题,加引号是最保守的,除非因为引号转义符导致什么偏差,但这是别的问题了。
    > In either case, quoting anyway will not break anything. So if in doubt, quote!

    看了 https://mywiki.wooledge.org/WordSplitting
    感觉 Word Splitting 和 IFS 真是麻烦啊....

    > 最后送楼主一句话:明知山有虎,……
    诶,从接触命令行到现在一直用的是 bash,而且 bash 在大多数系统里都是默认 shell,用 bash 已经变成我的执着。
    Kobayashi
        6
    Kobayashi  
       2019-06-18 17:40:36 +08:00
    有些地方可能让你造成了误解。

    引号的问题:

    k=("$(printf '\n\n\n')") 中,输出结果被引起来,这样 3 个换行被当做一个字符串,数组大小必然为 1.

    k="$(printf '\n\n\n')" 中,问题同上,不过这次没加括号,返回的根本不是数组,就是 1 个字符串。

    你在不该加引号的地方加入了引号,所以我才觉得你可能不大会用引号。先不提空字符串,这样做根本不可能捕捉到大小为 3 的数组。

    最后一句话不是指 Bash,而是指捕捉空字符串这个解决方案本身就棘手,换个思路不用它才是最好的做法。

    wooledge 维基我读的是 BashGuide 新手教程。单个知识点的话,可能会解释的更为详尽、繁琐,有时候没必要追究太深。
    adoyle
        7
    adoyle  
    OP
       2019-06-18 20:34:28 +08:00
    @Kobayashi 感谢细心指出,引号的问题如你所说,我之后也意识到这个问题,当时脑袋糊涂了,举了这个例子。

    捕捉空字符串这个需求是因为我简化了问题导致看起来没意义,实际上我是为了实现一个严格的 split 函数,返回一个数组,就可能会需要返回包含多个空字符串的数组。后来我觉得函数返回数组比较难以实现,就直接去改传入参数了。

    BashGuide 还没有读过,因为我之前直接读的 Bash Reference Manual。wooledge wiki 写得非常棒,能够把这些细节深挖到这地步,非常让人佩服。
    james122333
        8
    james122333  
       2019-07-05 17:08:46 +08:00
    #!/bin/bash

    k=($(echo "\n" "\n" "\n"))
    echo ${#k[@]}

    for i in ${k[@]}
    do
    echo -en "$i"
    done > log
    james122333
        9
    james122333  
       2019-07-05 17:20:43 +08:00
    感觉 newline 当空不是个好方法
    不是很正规
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1114 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 22ms · UTC 18:40 · PVG 02:40 · LAX 10:40 · JFK 13:40
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.