awk/sed 分隔最后一列并在中间列周围添加引号?

awk/sed 分隔最后一列并在中间列周围添加引号?

我有一个名为的文件test.txt,经过一些操作后它看起来像这样:

Metabolism
Global and overview maps
01100 Metabolic pathways (1689)
01110 Biosynthesis of secondary metabolites (677)
01120 Microbial metabolism in diverse environments (356)
01200 Carbon metabolism (44)
012111 Carbon metabolism (151) test: test test (44)

现在,我想用括号内的数字分隔最后一列,使它们成为一个单独的列(使用分号作为我选择的分隔符)。我还想在括号内的数字和开头的 ID 号之间的所有文本加上引号。最后,我想保留标题行(本例中的前两行)。

我的代码:

 sed -r 's/ +/;/' test.txt | awk 'NF{NF-=1};1' | awk -F ";" '{sub($2, "\"&\""); print}'

我当前的输出:

""
Global;"and overview"
01100;"Metabolic pathways"
01110;"Biosynthesis of secondary metabolites"
01120;"Microbial metabolism in diverse environments"
01200;"Carbon metabolism"
012111;Carbon (151) test: test test

正如您所看到的,“新陈代谢”标题消失了,因为从技术上讲,它是该行中的最后一个值,以及第二行中的“地图”,“全局”后面有一个分号,这是不需要的。有些行在文本中有括号内的数字,我应该保留这些数字,但否则所有行都以括号内的值结尾,该值应该分隔成由分号分隔的唯一列。我也无法让引号围绕最后一行中的所有第二列,而其他行都可以。最后,我不知道如何分隔括号内的值,使它们成为第三列。

我想要的输出(将数字保留为 sep 列):

"Metabolism"
"Global and overview"
01100:"Metabolic pathways";1689
01110:"Biosynthesis of secondary metabolites";677
01120:"Microbial metabolism in diverse environments";356
01200:"Carbon metabolism";44
012111:"Carbon metabolism (151) test: test test";44

使用 awk GNU 版本 4.1.3 和 sed GNU 版本 4.2.2。在 Windows Linux 子系统上

答案1

$ cat file
Metabolism
Global and overview maps
01100 Metabolic pathways (1689)
01110 Biosynthesis of secondary metabolites (677)
01120 Microbial metabolism in diverse environments (356)
01200 Carbon metabolism (44)
012111 Carbon metabolism (151) test: test test (44)
$ sed -e 's/^\([[:digit:]]*\)[[:blank:]]*/\1;"/' -e 's/[[:blank:]]*\((\([[:digit:]]*\))\)\{0,1\}[[:blank:]]*$/";\2/' file
;"Metabolism";
;"Global and overview maps";
01100;"Metabolic pathways";1689
01110;"Biosynthesis of secondary metabolites";677
01120;"Microbial metabolism in diverse environments";356
01200;"Carbon metabolism";44
012111;"Carbon metabolism (151) test: test test";44

这里使用的命令sed进行了两次替换:

  • s/^\([[:digit:]]*\)[[:blank:]]*/\1;"/
    这会替换行开头可能为空的一串数字,后跟零个或多个空格(制表符或空格)以及数字和分号。如果行首没有数字,则将在行首插入分号。它还在分号后面插入第二个字段的第一个双引号字符。

  • s/[[:blank:]]*\((\([[:digit:]]*\))\)\{0,1\}[[:blank:]]*$/";\2/
    这匹配任意数量的数字、其两侧的括号以及行末尾的初始空格(如果存在这样的带有数字的括号)。它还允许在该行的最末端留出额外的空白。它仅用匹配的数字替换匹配的文本。插入的数字前面是第二个字段的第二个双引号和;分隔符。

    你愿意吗消除最后一个字段中的数字,然后只需修改第二个sed表达式的替换文本(可能只是"而不是";\2)。

命令sed

sed -e 's/^\([[:digit:]]*\)[[:blank:]]*/\1;"/' \
    -e 's/[[:blank:]]*\((\([[:digit:]]*\))\)\{0,1\}[[:blank:]]*$/";\2/' file

可以使用扩展正则表达式和较短的括号表达式重写(如果我们假设我们只想匹配空格而不匹配制表符):

sed -E \
    -e 's/^([0-9]*) */\1;"/' \
    -e 's/ *(\(([0-9]*)\))? *$/";\2/' file

答案2

使用 GNU sed:

sed -e '1,2{p;d}' -e 's/ /;"/' -e 's/ ([[:digit:]]\+)$/"/' input

或更兼容:

sed -e '1{p;d;}' -e '2{p;d;}' -e 's/ /;"/' -e 's/ ([[:digit:]]\{1,\})$/"/' input

答案3

GNU sed在扩展正则表达式模式下使用-E,我们将标题行标识为不以括号数字结尾的标题行。假设:

  • 没有前导/尾随空格。
  • 没有连续的空格。
  • 输入具有 Unix 行结尾 (\n)
sed -Ee '
  /\s\(([0-9]+)\)$/!s/.*/;"&";/;t
  s//";\1/;s/\s/;"/
' file
;"Metabolism";
;"Global and overview maps";
01100;"Metabolic pathways";1689
01110;"Biosynthesis of secondary metabolites";677
01120;"Microbial metabolism in diverse environments";356

perl -lpe '
  s/\s\K\((\d+)\)$/$1/ ?
    s/\s(.*)\s/;"$1";/ :
    s/(.*)/;"$1";/     ;
' file

使用perl我们可以执行以下操作(与上面的假设相同)。

  • 分别将前 n 个最后字段存储在标量 $a $b 中,仅适用于以括号数字结尾的行。
perl -slane 'local($a,$b);
  ($a,$b) = (shift(@F),pop(@F))
    if /\s\(\d+\)$/;
  print $a, qq("@F"), $b =~ tr/()//dr;
' -- -,=\; file

awk '
$NF ~ /^\([0-9]+)$/ &&
p = match($0,/ .* /) {
  l = length($NF)
  mid = substr($0, p, RLENGTH)
  gsub(/^ | $/, "\"", mid)
  print $1, mid, substr($NF, 2, l-2)
}
!p&&(sub(/.*/, ";\"&\";")||1)
' OFS=\; file

答案4

如果您只想删除行尾的“(数字)”:

sed 's|\(^.*\) ([[:digit:]]*)$|\1|g' test.txt

会给你:

Metabolism  # this header I need to keep
Global and overview maps  # this header I need to keep
01100 Metabolic pathways
01110 Biosynthesis of secondary metabolites
01120 Microbial metabolism in diverse environments

相关内容