拆分一行的第二列以使用 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
简要说明:使用awk
的gsub
函数全局替换所有出现的[=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 中的第一行并重复。
我有一个制表符分隔的数据,它看起来像这样:
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
简要说明:使用awk
的gsub
函数全局替换所有出现的[=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 中的第一行并重复。