Bash 读取函数 returns 使用换行符时的错误代码

Bash read function returns error code when using new line delimiter

我有一个脚本,我正在 return 从中获取多个值,每个值占一行。为了将这些值捕获为 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 命令)以使用不同的分隔符(因为在代码的其他地方使用新行是有意义的)。

如何在成功读取 3 个值时让 read 正常运行并 return 零退出状态?

更新

嗯,看来我可能用错了 "delimiter"。来自手册页:

-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 在这里使用是错误的。您真正想要的是三个单独的读取调用:

{ 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

这里的 "problem" 是 read return 到达 EOF 时非零,这发生在分隔符不在输入末尾时.

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

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

为此使用 -d 没问题,但 \n 是默认分隔符,因此如果您这样做并且 ,则不会更改任何内容得到正确的定界符 (-d $'\n') 首先你会看到你的例子根本不起作用(尽管它会 returned 0 from read)。 (参见 http://ideone.com/MWvgu7

使用 read 时的一个常见习语(主要是 -d 的非标准值是测试 read 的 return 值 分配给的变量是否有值。例如read -d '' line || [ "$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"

并将[=32=]添加到输入流的末尾(例如printf '1 1\n2 2\n3 3[=33=]')或将|| [ "$a" ]放在末尾将避免失败return来自read 呼叫.

读取IFS的设置是为了防止shell在空格上进行分词并错误地分解我的输入。 -d ''[=32=]read