壳牌、IFS、阅读和制表

  • 本文关键字:IFS 壳牌 bash 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设置为"0";c";

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

以|作为分隔符,$v2得到一个空值,$v3得到一个值";c";正如我所期望的那样。

有人对使用|或\t时的不同行为有解释吗?还有一种让t表现得像|的方式?

有人对使用|或\t时的不同行为有解释吗?

从posix读取:

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

因此,让我们转到posix shell字段拆分(emphasis mine(:

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

  1. 如果IFS的值是<space><tab><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设置为空白空间的任何组合时,则在分割字段时将这些空白空间连接在一起(即"非zer长度"(。

所以echo -e "attc" | IFS=$'t' read v1 v2 v3等于echo -e "atttttc" | IFS=$'t' read v1 v2 v3。因为存在";是比vars"更少的字段;(2对3(,v3被设置为空字符串。

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

还有一个有趣的角落案例,空白字符被特殊处理。

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

在bash中,在读取之前将其替换为唯一的内容。我喜欢使用0x01字节:

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

记住使用read -r

相关内容

  • 没有找到相关文章

最新更新