使用tab作为分隔符将一行拆分为bash中的数组

use*_*373 10 bash array

我有以下格式的文件,它是制表符分隔的

a   k   testis  adult   male    8 week  rRNA
b   k   testis  adult   male    8 week  rRNA
c   k   testis  adult   male    8 week  rRNA
Run Code Online (Sandbox Code Playgroud)

我想在每一行上做一些操作,所以我使用了一个 while 循环。我想在选项卡上拆分每一行,然后存储让我们说第 6 列8 week的变量。我正在使用此代码,但无法获得我想要的

while read -r line; do tmp=(${line///}); col6=${tmp[5]}; echo "$col6"; done < file.txt
Run Code Online (Sandbox Code Playgroud)

这给了我8而不是8 week。8 周在 8 和周之间有一个空格,因此我想在选项卡上拆分该行。

ilk*_*chu 15

数组分配tmp=(${line///})将值拆分为IFS包含的任何字符,默认情况下包括制表符、空格和换行符。(我看不到空替换的作用。)要仅在选项卡上拆分,请设置IFS为:

foo=$'a\tk\testis\tadult\tmale\t8 week\tRNA'
IFS=$'\t'
tmp=($foo)
echo "${tmp[5]}"
Run Code Online (Sandbox Code Playgroud)

虽然这仍然留下通配符是一个问题,因为你已经在使用while read,你可以使用read -a tmp(Bash中只,更换-a-A使用的ksh / zsh的/佳日),它分裂基础上的输入线IFS,并将所产生的领域的元素命名数组:

$ while IFS=$'\t' read -r -a tmp ; do
    echo "${tmp[5]}"
done <<< $'a\tk\testis\tadult\tmale\t8 week\tRNA'
Run Code Online (Sandbox Code Playgroud)

那打印8 week. 这样做的另一个好处是,更改IFS仅在 的持续时间内有效read,而不对脚本的其余部分有效。

但是请注意,read使用制表符作为分隔符时会删除空字段。在 中zsh,您可以替换IFS=$'\t'IFS=$'\t\t'以阻止这种情况发生。

当然,如果我们知道字段的数量/含义,我们可以read将它们拆分为单独的命名变量:

... IFS=$'\t' read -r col1 col2 col3 ...
Run Code Online (Sandbox Code Playgroud)

或者,如果您只想打印那一列,请使用cut

cut -d$'\t' -f 6  < file.txt
Run Code Online (Sandbox Code Playgroud)

如果您有空列,cut -d$'\t'并且IFS=$'\t'对它们有不同的行为。Cut 会将每个单独的选项卡视为不同的分隔符,而read将连续的选项卡视为单个分隔符。也就是说,字符串foo<tab><tab>bar将被读取为两列read,但被读取为三列cut

您不能为制表符更改此设置,但打印字符始终被识别为不同的分隔符,因此您可以将制表符更改为某个未出现在数据中的字符,然后将其用作分隔符,例如... | tr '\t' : | IFS=: read -r -a tmp左右。

  • @user3138373 `-a` 标志告诉 `read` 将它读取的内容存储为数组。 (2认同)