2014-01-17 16 views
0

假设我们有一个至少有2个换行符的字符串,我们需要将它分解为三个字符串,其中第一个可能包含换行符,但以下两个不能。为什么在本例中'read'不接受 0作为分隔符?

$ 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) - 发行版(x86_64-pc-linux-gnu)。

GNU sed的版本4.2.1

+0

我们可以推测的是,分隔符被存储为空终止字符串,并且该'read'逻辑失败的未初始化和显然空区分值,并在这两种情况下回退到“IFS”分裂。也许提交一个错误报告? – tripleee

+1

它应该在bash中工作。看到这个http://mywiki.wooledge.org/BashFAQ/020 –

+0

@ aleks-daniel-jakimenko所以这是我应该报告的错误? //也用newline测试更新我的文章 – tijagi

回答

2

-d指定了每个 “线” 应分开。每个“行”是,然后按IFS中的字符分隔并放入您指定的变量。所以,如果你

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

字符串被读取到第一个NULL字符,然后根据IFS分裂,从而导致:

a: foo 
b: bar 
c: baz 

拆就我用下面的模式NULL字符:

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--- 
+0

非常感谢!现在对我来说很清楚。这是一个很好的答案,我非常感谢。 – tijagi

+0

此外,这是我的错误,不添加完成'\ x00'终止最后一个字符串。 – tijagi

相关问题