【问题标题】:Fill a bash array from a NUL separated input从 NUL 分隔的输入中填充 bash 数组
【发布时间】:2014-06-21 20:30:54
【问题描述】:

我想从 NUL 分隔的输入(来自标准输入)创建一个 bash 数组。

这是一个例子:

## Let define this for clarity
$ hd() { hexdump -v -e '/1 "%02X "'; echo ;}
$ echo -en "A B\0C\nD\0E\0" | hd
41 20 42 00 43 0A 44 00 45 00

这是我的意见。

现在,如果不使用 -aread 命令,使用 NUL 可以正常工作:

$ while read -r -d '' v; do echo -n "$v" | hd; done < <(echo -en "A B\0C\nD\0E\0")
41 20 42 
43 0A 44 
45 

我们得到正确的值。但我无法使用-a 存储这些值:

$ read -r -d '' -a arr < <(echo -en "A B\0C\nD\0E\0")
$ declare -p arr
declare -a arr='([0]="A" [1]="B")'

这显然不是我想要的。我想要:

$ declare -p arr
declare -a arr='([0]="A B" [1]="C
D" [2]="E")'

有没有办法使用read -a,如果它不起作用,为什么?你知道一个简单的方法来做到这一点(避免while 循环)?

【问题讨论】:

  • 为什么要避免while循环? while 循环是常见问题解答批准的 irc.freenode.org/#bash-blessed 正确方法。
  • ...请注意,我更喜欢 readarraymapfile 支持 NUL 分隔符,但从 Bash 4.3 开始,它们不支持。也许有人应该问切特是否会接受补丁......
  • 我正在使用while 循环。我只是想知道为什么这不起作用,并且不想确保我没有遗漏一些明显的东西。任何可以提供有关“为什么”的更多信息的详细信息(错误报告、源代码链接、操作系统限制、来源确认)?
  • -d 提供了 read -a 使用的分隔符来告诉它何时完全停止阅读,而不是何时停止阅读单个条目。这会让行为更清晰吗?

标签: arrays bash stdin nul


【解决方案1】:

这是@vaab 函数的简化。它使用bash 4.3's nameref 功能:

read_array () {
  local -n a=$1
  while read -r -d '' value; do
    a+=("$value")
  done
}

测试:

test_it () {
  local -a arr
  read_array arr < <(echo -en "A B\0C\nD\0E\0")
  declare -p arr
}
test_it

【讨论】:

    【解决方案2】:

    bash-4.4-alpha 为mapfile 添加了-d 选项:

    `mapfile' 内置现在有一个 -d 选项来使用任意 特点 作为记录分隔符,以及将分隔符剥离为的 -t 选项 随 -d 提供。

    ——https://tiswww.case.edu/php/chet/bash/CHANGES

    使用它,我们可以简单地写:

    mapfile -t -d '' arr < <(echo -en "A B\0C\nD\0E\0")
    

    【讨论】:

    • 有用的附录。也就是说,我建议用printf '%s\0' "A B" C D E 代替echo,顺便说一句——即使在bash 上,echo -e 并不总是可用(例如,只要xpg_echo 两者都在输出上打印-eposix 标志处于活动状态——前者可以在编译时设为默认值)。
    【解决方案3】:

    如果有人想知道,这里是我用来存储来自NUL-分隔stdin 的值的函数(使用while):

    read_array () {
        local i
        var="$1"
        i=0
        while read -r -d '' value; do
            printf -v "$var[$i]" "%s" "$value"
            i=$[$i + 1]
        done
    }
    

    然后可以非常干净地使用它:

    $ read_array arr < <(echo -en "A B\0C\nD\0E\0")
    $ declare -p arr
    declare -a arr='([0]="A B" [1]="C
    D" [2]="E")'
    

    【讨论】:

    【解决方案4】:

    read -a 是错误的工作工具,正如您所注意到的;它只支持非 NUL 分隔符。适当的技术在BashFAQ #1中给出:

    arr=()
    while IFS= read -r -d '' entry; do
      arr+=( "$entry" )
    done
    

    为什么而言,read -d '' -a 是错误的工具:-dread 提供了一个参数,用于确定何时完全停止阅读,而不是何时停止阅读单个元素.

    考虑:

    while IFS=$'\t' read -d $'\n' words; do
      ...
    done
    

    ...这将读取由制表符分隔的单词,直到它到达换行符。因此,即使使用read -a,使用-d '' 也会读取直到它达到NUL

    您想要阅读的内容,直到没有更多内容可用并被 NUL 分割,不是 NUL 的“-d”,而是根本没有行尾字符(以及一个空的 IFS)。这不是 read 的使用当前可用的东西。

    【讨论】:

    • 您可能想指向BashFAQ #5。因为 #1 没有谈到数组。
    • @vaab, #1 直接 用于读取 NUL 分隔的输入。查找描述正确使用 find -print0 的示例。
    • #1 中只提到了一个单词“array”,它告诉我们转到#5。我觉得#5回答了我的担忧,而不是#1。我明确知道如何使用read 阅读 NUL 分隔的内容,如问题本身所示。
    • @vaab,它没有说“数组”,但它确实说的是 NUL 分隔的文本。搜索-print0
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-26
    • 1970-01-01
    • 2019-04-26
    • 1970-01-01
    • 2012-04-16
    相关资源
    最近更新 更多