使用换行符时 Bash 读取函数返回错误代码

Lee*_*ton 5 bash built-in io-redirection

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

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

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

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

注意退出状态 1。

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

read当它成功读取 3 个值时,我如何玩得很好并返回零退出状态?

更新

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

-d *delim* 

  The first character of delim is used to terminate the input line,
  rather than newline.
Run Code Online (Sandbox Code Playgroud)

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

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

也许有更好的方法?

Eta*_*ner 5

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

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

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

使用-d这个很好,但它\n是默认的分隔符,所以如果你这样做,你就不会改变任何东西,并且如果你首先得到了正确的分隔符(),你会发现你的示例根本不起作用(尽管会从读取-d $'\n'返回)。0(参见http://ideone.com/MWvgu7

read使用(大多使用非标准值 for时的常见习惯用法-d是测试 的read返回值以及分配给的变量是否具有值。read -d '' line || [ "$line" ]例如。即使由于以下原因在输入的最后“行”上读取失败,该习惯用法也可以工作结尾处缺少终结符。

因此,为了让您的示例正常工作,您需要按照readchepner 指示的方式使用多个调用,或者(如果您确实想要单个调用)那么您需要(请参阅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"
Run Code Online (Sandbox Code Playgroud)

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

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


che*_*ner 3

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

{ read a; read b; read c; } < <(echo $'1\n2\n3\n')
Run Code Online (Sandbox Code Playgroud)

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

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

readarray -t arr < <(echo $'1\n2\n3\n')
Run Code Online (Sandbox Code Playgroud)

在4之前bash,需要使用循环:

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

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

$ while read -d'#' value; do
>    echo "$value"
> done << EOF
> a#b#c#
> EOF
a
b
c
Run Code Online (Sandbox Code Playgroud)