Shell、IFS、读取和制表

我试图在 shell 脚本中读取 TSV 文件,发现当 IFS 设置为制表时,读取跳过空值。一个例子胜过 1000 字:

$ echo -e "atbtc" | while IFS=$'t' read v1 v2 v3; do echo "$v1 - $v2 - $v3"; done
a - b - c

这项工作按预期进行

$ echo -e "attc" | while IFS=$'t' read v1 v2 v3; do echo "$v1 - $v2 - $v3"; done
a - c - 

我本来希望将 $v2 设置为 null,将 $v3 设置为“c”

$ echo -e "a||c" | while IFS=$'|' read v1 v2 v3; do echo "$v1 - $v2 - $v3"; done
a -  - c

与 | 作为分隔符,$v2 获得空值,$v3 获得值“c”,正如我所期望的。

任何人都可以解释使用 | 时的不同行为 或 t ?还有一种让 t 表现得像 for | 的方法 ?

回答

任何人都可以解释使用 | 时的不同行为 或 t ?

从posix 读到:

该行应像在 shell 中一样拆分为字段(请参阅字段拆分);第一个字段应分配给第一个变量 var,第二个字段应分配给第二个变量 var,依此类推。如果指定的 var 操作数少于字段数,则剩余的字段及其中间分隔符应分配给最后一个 var。如果字段少于 vars,则剩余的 vars 应设置为空字符串。

那么让我们进入posix shell 字段拆分(重点是我的):

Shell 将 IFS 的每个字符视为一个分隔符,并使用分隔符将参数扩展和命令替换的结果拆分为字段。

  1. 如果IFS 的值为 a <space>, <tab>, and <newline>,或者未设置,... [在此处不适用]
  2. 如果IFS 的值为 null,... [这里也不适用]
  3. 否则,以下规则将依次适用。术语“IFS空白”被用来表示是在IFS值(例如,如果空白字符的任何序列(零个或多个实例)IFS包含<space>/ <comma>/<tab>中任序列<space>S和<tab>s是考虑IFS白色空间)。
    1. IFS 空白在输入的开头和结尾应被忽略。
    2. 非 IFS 空格IFS字符输入中的每次出现,以及任何相邻的IFS空格,都应界定一个字段,如前所述。
    3. 非零长度的IFS空白应界定一个字段。

IFS设置为空格的任意组合时,这些空格在拆分字段时会连接在一起(即“非零长度”)。

所以echo -e "attc" | IFS=$'t' read v1 v2 v3等于echo -e "atttttc" | IFS=$'t' read v1 v2 v3。因为“字段比变量少”(2 对 3),v3所以设置为空字符串。

但是当IFS设置为除空白之外的任何其他内容时,该字符的每次出现都会IFS拆分字段。

另一个有趣的角落案例,其中对空白字符进行了特殊处理。

还有一种让 t 表现得像 for | 的方法 ?

在 bash 中,在阅读之前将其替换为独特的东西。我喜欢使用0x01字节:

echo -e "attc" |
    tr 't' $'x01' |
    while IFS=$'x01' read -r v1 v2 v3; do echo "$v1 - $v2 - $v3"; done

记得使用read -r.


以上是Shell、IFS、读取和制表的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>