我有一个以下格式的文件,它是制表符分隔的
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 替换-a
with -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
左右。