Shell读取*有时*去除尾随定界符

时间:2018-10-11 14:09:31

标签: bash shell sh

要解析冒号分隔的字段,我可以将read与自定义IFS结合使用:

$ echo 'foo.c:41:switch (color) {' | { IFS=: read file line text && echo "$file | $line | $text"; }
foo.c | 41 | switch (color) {

如果最后一个字段包含冒号,则没问题,冒号将保留。

$ echo 'foo.c:42:case RED: //alert' | { IFS=: read file line text && echo "$file | $line | $text"; }
foo.c | 42 | case RED: //alert

还保留了尾部定界符...

$ echo 'foo.c:42:case RED: //alert:' | { IFS=: read file line text && echo "$file | $line | $text"; }
foo.c | 42 | case RED: //alert:

...除非它是 only 额外的分隔符。然后将其剥离。 等等,

$ echo 'foo.c:42:case RED:' | { IFS=: read file line text && echo "$file | $line | $text"; }
foo.c | 42 | case RED

Bash,ksh93和破折号都可以做到这一点,所以我猜这是POSIX标准行为。

  1. 为什么会发生?
  2. 什么是最好的选择?

我想将上面的字符串解析为三个变量,并且我不想破坏第三字段中的任何文本。我以为read是应该走的路,但是现在我正在重新考虑。

2 个答案:

答案 0 :(得分:2)

是的,这是标准行为(请参见read specificationField Splitting)。一些外壳程序(至少基于ash,包括dash,基于pdkshzshyash的外壳程序除外,但除了{ zsh(当未处于POSIX模式时),busybox sh,其中大多数已更新为符合POSIX。

对于:

$ var='a:b:c:' IFS=:
$ set -- $var; echo "$#"
3

(请参阅read的POSIX规范实际上如何遵循 Field Splitting 机制,其中a:b:c:被分为3个字段,对于IFS=: read -r a b c,字段和变量一样多。

基本原理是,在ksh(POSIX规范所基于的)$IFS(最初在Bourne shell中,内部字段分隔符)成为了字段定界符,我认为因此可以表示任何元素列表(不包含定界符)。

$IFS分隔符时,不能代表一个空元素的列表(""被分成0个元素的列表,{{1} }分为两个空元素的列表¹)。当它是定界符时,可以用":"表示零元素的列表,或者用""表示一个空元素,或者用":"表示两个空元素。 / p>

不幸的是,"::"的最常见用法之一是拆分$IFS。像$PATH这样的$PATH应当分为/bin:/usr/bin:"/bin""/usr/bin",而不仅仅是"""/bin"

现在,有了POSIX外壳程序(但不是所有外壳程序都兼容),可以在参数扩展时进行单词拆分,可以解决以下问题:

"/usr/bin"

尾随IFS=:; set -o noglob for dir in $PATH""; do something with "${dir:-.}" done 确保如果""以尾随$PATH结尾,则添加了一个额外的空元素。而且,将空:视为应为一个空元素。

虽然这种方法不能用于$PATH

切换到read的短暂时间,除了插入一个额外的zsh并随后将其删除外,没有其他容易的解决方法,例如:

:

或(便携式程度较低):

echo a:b:c: | sed 's/:/::/2' | { IFS=: read -r x y z; z=${z#:}; echo "$z"; }

我还添加了使用echo a:b:c: | paste -d: - /dev/null | { IFS=: read -r x y z; z=${z%:}; echo "$z"; } 时通常需要的-r

最可能在you'd want to use a proper text processing utility like sed/awk/perl instead of writing convoluted and probably inefficient code around read,其中has not been designed for that


¹尽管在Bourne外壳程序中,它仍被分成零个元素,因为在那里IFS空格字符和IFS非非空格字符之间没有区别,ksh也添加了一些东西 < / p>

答案 1 :(得分:0)

read的一个“特征”是它将strip leading and trailing whitespace separators填充到它所填充的变量中-在链接的答案中有更详细的解释。这样一来,初学者就可以read做他们期望的事情,例如read first rest <<< ' foo bar '(注意多余的空格)。

外卖?使用Bash和Shell工具很难进行准确的文本处理。如果要完全控制,最好使用Python等“严格”语言,其中split()将满足您的要求,但是您可能需要更深入地研究字符串处理以显式删除换行符或处理编码。