为什么'read'在这个例子中不接受\ 0作为分隔符?

时间:2014-01-17 04:06:49

标签: bash

假设我们有一个至少包含2个换行符的字符串,我们需要将其分成三个字符串,其中第一个字符串可能包含换行符,但后面的两个字符串不能。

$ echo -ne '1\n2\n3\n4\n5' |\
    sed -rn '1h; 2,$ H;  # Combine all strings in hold space
             ${g;        # hold space → pattern space
             s/^(.*)\n([^\n]+)\n([^\n]+)$/\1\x00i\x00ii/g;
             p}' >/tmp/h

$ hexdump -C /tmp/h
00000000  31 0a 32 0a 33 00 69 00  69 69                    |1.2.3.i.ii|
0000000a

现在,正如我们在hexdump中看到的那样,数据是正确的。但是,如果我们将这些字符串放入内置读取中,则无法按预期工作。

$ read -d $'\0' a b c < /tmp/h \
  && echo -e "---$a---\n+++$b+++\n===$c==="
---1---
+++2+++
===3===

与实际命令相同

$ read -d $'\0' a b c < <(echo -ne '1\n2\n3\n4\n5' |\
    sed -rn '1h; 2,$ H; 
    ${g;s/^(.*)\n([^\n]+)\n([^\n]+)$/\1\x00i\x00ii/g;p}' );\
  echo -e "---$a---\n+++$b+++\n===$c==="
---1---
+++2+++
===3===

更有趣:它不会将换行视为分隔符

echo ' - - - - - - - - - - - - - - - - - No delimiter'
unset a b c
read  a b c < <(seq 1 18 | sed -rn '4,+2 p')
echo -e "---${a:-not set}---\n---${b:-not set}---\n---${c:-not set}---"

echo ' - - - - - - - - - - - - - - - - - Delimiter is $ \n  '
unset a b c
read -d $'\n' a b c < <(seq 1 18 | sed -rn '4,+2 p')
echo -e "---${a:-not set}---\n---${b:-not set}---\n---${c:-not set}---"

echo ' - - - - - - - - - - - - - - - - - Delimiter is "$ \n"'
unset a b c
read "-d $'\n'" a b c < <(seq 1 18 | sed -rn '4,+2 p')
echo -e "---${a:-not set}---\n---${b:-not set}---\n---${c:-not set}---"

echo ' - - - - - - - - - - - - - - - - - Delimiter is $ \0 '
unset a b c
read -d $'\0' a b c < <(seq 1 18 | sed -rn '4,+2 p')
echo -e "---${a:-not set}---\n---${b:-not set}---\n---${c:-not set}---"

输出:

 - - - - - - - - - - - - - - - - - No delimiter
---4---
---not set---
---not set---
 - - - - - - - - - - - - - - - - - Delimiter is $ \n  
---4---
---not set---
---not set---
 - - - - - - - - - - - - - - - - - Delimiter is "$ \n"
---4---
---5---
---6---
 - - - - - - - - - - - - - - - - - Delimiter is $ \0 
---4---
---5---
---6---

不,我没有改变IFS。

GNU bash,版本4.2.45(1)-release(x86_64-pc-linux-gnu)。

GNU sed版本4.2.1

1 个答案:

答案 0 :(得分:2)

-d指定每个“行”应分隔的内容。每个“行”然后由IFS中的字符拆分并放入您指定的变量中。所以,如果你这样做

read -d '' a b c <<< $'foo bar\nbaz\0next line'
printf 'a: %s\nb: %s\nc: %s' "$a" "$b" "$c"

将字符串读取到第一个NUL字符,然后根据IFS进行拆分,从而产生:

a: foo
b: bar
c: baz

要拆分NUL字符,我使用以下模式:

IFS= read -r -d ''

这是extensively tested文件名包含换行符和其他输入的内容。

如果您希望它使用不以终结符结尾的字符串,则必须在最后添加|| [ -n "$REPLY" ]

如果是第一次测试:

$ while IFS= read -d $'\0' value || [ -n "$value" ]; do echo ---$value---; done < /tmp/h
---1 2 3---
---i---
---ii---