我有一个制表符分隔的文件,其中有一个数字,并且同一行上有属于同一个数字的名称。数字和名称由制表符分隔。名称通过 2 个下划线 ( ) 相互链接__
。它看起来像这样:
33 Hhe.1__Hhe.2__Hhe.3__Hhe.4
我想将其(通过使用命令行)转换为以下输出:
33 Hhe.1
33 Hhe.2
33 Hhe.3
33 Hhe.4
答案1
和awk
:
$ awk -F '\t|__' '{for (i=2;i<=NF;i++) {printf "%s\t%s\n", $1, $i}}' foo.txt
33 Hhe.1
33 Hhe.2
33 Hhe.3
33 Hhe.4
\t
我们根据制表符 ( ) 或两个下划线 ( )将行拆分为字段__
。- 然后我们循环从第二个字段到最后一个字段,并打印每个以第一个字段和制表符为前缀的字段。
答案2
您可以使用 perl 单行命令来实现这一点:
perl -ane '@l=split(/__/,$F[1]); foreach $val (@l){print $F[0],"\t",$val,"\n"}'
例子:
$ echo "33 Hhe.1__Hhe.2__Hhe.3__Hhe.4" | perl -ane '@l=split(/__/,$F[1]); foreach $val (@l){print $F[0],"\t",$val,"\n"}'
33 Hhe.1
33 Hhe.2
33 Hhe.3
33 Hhe.4
所用命令的解释:
perl -ane #read input line-wise and split line on tab
'@l=split(/__/,$F[1]); #split the second element ($F[1]) on a double _
foreach $val (@l){ #for each value, print the first element and the value.
print $F[0],"\t",$val,"\n"
}'
答案3
另一种使用 Perl 的方法:
perl -lane '$,="\n"; print(map($F[0] . "\t" . $_, split("__", $F[1])))' file
perl -lane '
$,="\n";
print(map($F[0] . "\t" . $_, split("__", $F[1])))
' file
-l[octnum]
: 启用自动行结束处理。它有两个单独的效果。首先,当与或$/
一起使用时,它会自动 chomps(输入记录分隔符)。其次,它分配(输出记录分隔符)以具有 octnum 的值,以便任何打印语句都会重新添加该分隔符。如果省略 octnum,则设置为 的当前值。-n
-p
$\
$\
$/
-a
-n
:与或一起使用时打开自动拆分模式-p
。对数组的隐式拆分命令@F
是 或 产生的隐式 while 循环中的第一-n
件事-p
。-n
:导致 Perl 假设您的程序中存在以下循环,这使得它像sed -n
或一样迭代文件名参数awk
:LINE: while (<>) { ... # your program goes here }
-e
:可用于输入一行程序;$,="\n"; print(map($F[0] . "\t" . $_, split("__", $F[1])))
;将输出字段分隔符设置为换行符,拆分第二个字段__
并将第一个字段添加到每个子字段的前面,然后添加制表符,最后打印记录。
% cat file
33 Hhe.1__Hhe.2__Hhe.3__Hhe.4
% perl -lane '$,="\n"; print(map($F[0] . "\t" . $_, split("__", $F[1])))' file
33 Hhe.1
33 Hhe.2
33 Hhe.3
33 Hhe.4
答案4
与 muru 建议的类似:如何根据切片拆分第二个字段__
,然后循环切片?
awk 'BEGIN{FS=OFS="\t"}
{n=split($2,a,"__"); for (i=1;i<=n;i++) print $1, a[i]}' file
这利用了以下事实split()
返回创建的元素的数量。
此外,它将输入和输出字段分隔符设置为制表符,这样您在输入时就不必提及它print
。事实上,FS
这里不需要设置,因为FS
默认包含空格和制表符。
它返回:
33 Hhe.1
33 Hhe.2
33 Hhe.3
33 Hhe.4