Shell、IFS、读取和制表

问题描述

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

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

这项工作符合预期

$ echo -e "a\t\tc" | 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 read

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

那么让我们转到posix shell field splitting(强调我的):

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

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

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

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

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

另一个有趣的角落案例,其中空白字符被特殊对待。

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

在 bash 中,在阅读之前将其替换为独特的内容。我喜欢使用 IFS 字节:

0x01

记得使用 echo -e "a\t\tc" | tr '\t' $'\x01' | while IFS=$'\x01' read -r v1 v2 v3; do echo "$v1 - $v2 - $v3"; done

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...