拆分一行的第二列以使用 bash oneliner 创建多行

Splitting second column of a line to create multiple lines with a bash oneliner

我有一个制表符分隔的数据,它看起来像这样:

a   1a,2x,c1
b2  a4,4.6
3c  323

第二列有多个逗号分隔值。 我想得到这个输出:

a   1a
a   2x
a   c1
b2  a4
b2  4.6
3c  323

我能够用我写的 python 代码做到这一点:

import sys
f = sys.argv[1]

with open(f) as f:
    for line in f:
        line = line.strip("\n").split("\t")
        genes = line[1].split(",")
        for gene in genes:
            print(line[0],gene, sep="\t")

我知道我可以用 bash 脚本做同样的事情,但我想知道如何用一个很酷的 bash oneliner,使用 awk、sed、tr and/or 不使用 for 循环进行剪切。

我不能再进一步了:

tr ',' '\n' data

$ awk -F$'\t' '{split(,arr,","); for(e in arr) print(, arr[e])}' file
a 1a
a 2x
a c1
b2 a4
b2 4.6
3c 323

如果您想要第 1 列和第 2 列之间的制表符:

awk -F$'\t' '{split(,arr,","); for(e in arr) print( "\t" arr[e])}'
a   1a
a   2x
a   c1
b2  a4
b2  4.6
3c  323

编辑: 根据 OP 的要求,将不使用循环(仅使用提供的样本进行测试和编写),(公平警告:gsub 带有管道的版本是 OP 的好奇心,它比仅使用 for 循环并将所有处理保持在 awk 内更脆弱和更慢):

awk '{gsub(/,/,ORS  OFS)} 1'  Input_file | column -t

简要说明:使用awkgsub函数全局替换所有出现的[=16] =] 在每一行中使用 ORS(默认情况下为新行它的值)$1(根据 OP 的要求的第一个字段)OFS(space 默认情况下它的值)。然后提到 1 将在此处打印 edited/non-edited 行。然后将 awk 命令的输出传递给 column 命令以使用相同的 space.

美化其输出

能否请您尝试以下。

awk '{num=split(,array,",");for(i=1;i<=num;i++){print ,array[i]}}' Input_file
$ awk -F'[\t,]' '{for (i=2;i<=NF;i++) printf "%s\t%s\n", , $i }' file
a       1a
a       2x
a       c1
b2      a4
b2      4.6
3c      323

使用制表符和逗号作为字段分隔符并从第二个字段开始遍历字段。打印由制表符分隔的第一个字段和循环字段值。

GNU sed

sed -E ':a; s/([^ ]+) *([^,]+),([^,]+)/ \n /; ta' infile

说明

这通过用括号重复匹配 3 个组来实现。

  • ([^ ]+) 匹配到第一个 space
  • ([^,]+) 匹配到第一个逗号
  • ([^,]+) 最多匹配一个可能的第二个逗号
  • \n 替换第一组匹配的内容,然后替换第二组,然后换行,然后替换第一组和第三组

便携式 sed

parse.sed

:a
s/([^ ]+) *([^,]+),([^,]+)/ \
 /
ta

运行 像这样:

sed -Ef parse.sed infile

两种情况下的输出:

a 1a
a 2x
a c1
b2 a4
b2 4.6
3c  323

为避免编写循环,使用 GNU awk 进行多字符 RS:

$ awk -v RS='[,\n]' 'NF>1{k=} {print k, $NF}' file
a 1a
a 2x
a c1
b2 a4
b2 4.6
3c 323

相比之下,循环更清晰、更简单,并且可以在每个 UNIX 机器上的任何 shell 中与任何 awk 一起工作:

$ awk -F'[[:space:]]+|,' '{for (i=2; i<=NF; i++) print , $i}' file
a 1a
a 2x
a c1
b2 a4
b2 4.6
3c 323

在性能方面不会有显着差异,它们都运行足够快。

这可能对你有用 (GNU sed):

sed -E 's/^((.*\t)[^,]+),/\n/;P;D' file

将前两个制表符分隔值后跟逗号替换为前两个制表符分隔值、换行符和第一个值后跟制表符。打印并删除模式 space 中的第一行并重复。