在 bash 中将一行拆分为数组,并使用制表符作为分隔符

在 bash 中将一行拆分为数组,并使用制表符作为分隔符

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

a   k   testis  adult   male    8 week  rRNA
b   k   testis  adult   male    8 week  rRNA
c   k   testis  adult   male    8 week  rRNA

我想在每一行上做一些操作,所以我使用 while 循环。我想在选项卡上分割每一行,然后存储假设8 week在变量中的第六列。我正在使用这段代码,但我无法得到我想要的

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

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

答案1

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

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

尽管这仍然使通配符成为一个问题,并且由于您已经在使用while read,您可以使用read -a tmp(仅在 Bash 中,用 ksh/zsh/yash 替换-awith -A),它根据 分割输入行IFS,并将结果字段存储为命名数组:

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

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

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

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

... IFS=$'\t' read -r col1 col2 col3 ...

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

cut -d$'\t' -f 6  < file.txt

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

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

相关内容