【问题标题】:Bash read function returns error code when using new line delimiterBash 读取函数在使用换行符时返回错误代码
【发布时间】:2016-01-21 18:42:57
【问题描述】:

我有一个脚本,我从中返回多个值,每个值都在一个新行上。为了将这些值捕获为 bash 变量,我使用了 read 内置函数(推荐使用 here)。

问题是当我使用换行符作为read 的分隔符时,我似乎总是得到一个非零的退出代码。这对我的其他脚本造成了严重破坏,这些脚本会检查操作的结果。

这是我正在做的事情的精简版:

$ read -d '\n' a b c < <(echo -e "1\n2\n3"); echo $?; echo $a $b $c
1
1 2 3

注意1的退出状态。

我不想重写我的脚本(上面的 echo 命令)以使用不同的分隔符(因为在代码的其他位置使用新行是有意义的)。

如何让read 在成功读取 3 个值时发挥出色并返回零退出状态?

更新

嗯,看来我可能错误地使用了“分隔符”。从手册页:

-d *delim* 

  The first character of delim is used to terminate the input line,
  rather than newline.

因此,我可以达到预期结果的一种方法是这样做:

read -d '#' a b c < <(echo -e "1\n2\n3\n## END ##"); echo $?; echo $a $b $c

也许还有更好的方法?

【问题讨论】:

  • 你应该写-d $'\n'。否则,分隔符是反斜杠(可能永远不会真正读取,因为您没有使用read-r 选项)。

标签: bash io-redirection built-in


【解决方案1】:

这里的“问题”是read 在到达EOF 时返回非零值,这种情况发生在分隔符不在输入末尾时。

因此,在您的输入末尾添加换行符将使其按您期望的方式工作(并将参数修复为 -d,如 gniourf_gniourf 的评论中所示)。

在您的示例中发生的情况是 read 正在扫描 \ 并在找到它之前点击 EOF。然后输入行在\n 上拆分(因为IFS)并分配给$a$b$c。然后read 返回非零值。

为此使用-d 很好,但\n 是默认分隔符,因此如果您这样做并且已经得到正确的分隔符(-d $'\n'),则不会更改任何内容首先,您会看到您的示例根本不起作用(尽管它会从读取中返回0)。 (见http://ideone.com/MWvgu7

使用read时的一个常见习语(-d的非标准值大多是测试read的返回值分配给的变量是否有值。例如read -d '' line || [ "$line" ]。即使在输入的最后“行”由于末尾缺少终止符而导致读取失败时,它仍然有效。

因此,为了让您的示例正常工作,您希望按照 chepner 指示的方式使用多个 read 调用,或者(如果您真的想要一个调用)然后您想要(参见 http://ideone.com/xTL8Yn):

IFS=$'\n' read -d '' a b c < <(printf '1 1\n2 2\n3 3')
echo $?
printf '[%s]\n' "$a" "$b" "$c"

并且将\0 添加到输入流的末尾(例如printf '1 1\n2 2\n3 3\0')或将|| [ "$a" ] 放在末尾将避免read 调用的失败返回。

IFS 的读取设置是为了防止 shell 在空格上分词并错误地分解我的输入。 -d ''\0 上是 read

【讨论】:

    【解决方案2】:

    -d 在这里使用是错误的。您真正想要的是三个单独的读取调用:

    { read a; read b; read c; } < <(echo $'1\n2\n3\n')
    

    确保输入以换行符结束,这样最终的read 的退出状态为 0。

    如果您事先不知道输入中有多少行,则需要将值读入一个数组。在bash 4 中,只需一次调用readarray

    readarray -t arr < <(echo $'1\n2\n3\n')
    

    bash4之前,需要使用循环:

    while read value; do
        arr+=("$value")
    done < <(echo $'1\n2\n3\n')
    

    read 总是读取单行输入; -d 选项改变了read 对终止行的想法。一个例子:

    $ while read -d'#' value; do
    >    echo "$value"
    > done << EOF
    > a#b#c#
    > EOF
    a
    b
    c
    

    【讨论】:

    • 这很棒。我没有想过使用多个reads。谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-05-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-30
    • 1970-01-01
    相关资源
    最近更新 更多