【问题标题】:sed/awk merge pairs of lines into one linesed/awk 将成对的行合并为一行
【发布时间】:2016-05-18 16:54:00
【问题描述】:

我有一个数据列表,格式如下:

Joined       : a
Whatever     : b
Last visited : c
Useful       : y
NotUseful1   : f
Email        : z
RandomWat    : g
*** end *** 
Joined       : a
Whatever     : b
Last visited : c
Useful       : y
NotUseful1   : f
Email        : z
RandomWat    : g
*** end ***
Joined       : h
Whatever     : i
Last visited : j
Useful       : k
NotUseful1   : l
Email        : m
RandomWat    : n

我想要的是:

Useful, Email, Joined, Last Visited
y,z,a,c
y,z,a,c
k,m,h,j

我可能喝了太多啤酒,但我不知道如何在 fiex-length 列中跨行获取值。实际上是某些特定输出的 CSV 格式。

我确实在 sed/awk 中寻找一些东西,尽管任何文本处理器都可以。

【问题讨论】:

  • 是否有多个记录?如果是,它们是如何分开的?
  • 但是你的字段是用换行符分隔的,不是吗?空行?
  • 编辑的问题更清楚一点..希望..
  • 文字是*** end ***吗?
  • “Matt”等在您的预期输出中是从哪里出现的?编辑您的问题,以确保您的预期输出与您发布的示例输入完全相同。

标签: bash unix awk sed


【解决方案1】:

您可以将 Awk 的记录分隔符设置为 **end** 并通过一些简单的解析来选择字段;但如果格式是一个完全稳定的、按固定顺序排列的八行系列,那么你真正需要的是

awk -F ' *: *' -v OFS=, '{i=NR%8; a[i]=$NF }
    i==7 { print a[4], a[7], a[1], a[3] }' file

如果您的值可能包含冒号,则需要进行调整——这个简单的代码只是提取了最后一个冒号之后的内容。

添加 CSV 标题作为练习(或不添加)。

-F 选项设置输入字段分隔符,因此 Awk 将在冒号上分割行,修剪任何相邻的空白。 OFS 是输出字段分隔符;我们希望输出以逗号分隔。 NR 是当前输入的行号,% 8 计算模数;所以i 的值从 1 变为 7,然后回绕到零,然后再次开始爬升,在整个文件中重复。现在,我们使用i 作为数组a 的索引,并将每一行的最后一个字段收集到这个数组中。当我们到达索引 7 时,我们拥有了所有需要的字段,因此我们打印它们。 (我最初的值为 0,但那会丢失最后一条记录,因为您的示例数据中没有终止 **end**。)

NF 是输入字段的数量,我们希望它基本上一直是 2,所以我也可以在那里硬编码 $2。如果您需要扩展它,我会完全忘记-F ' *: *',只需手动sub("^[^:]*: *", ""),然后将整个值放在$0 中。

【讨论】:

    【解决方案2】:

    如果我理解正确,您希望在一行上打印所有第 1 列,然后在其下方的下一行打印第 2 列中的相应信息。

    使用 awk 非常简单。

    awk '{ORS=" "} {print$1}' file.txt
    awk '{ORS=" "} {print$NF}' file.txt
    

    输出:

    joined whatever last useful notuseful randomwat
    a b c y f z g
    

    ORS 是输出记录分隔符。默认为换行符。但在本例中,我们将其设为单个空格。

    所以对于每一行,awk 将打印每行的第一个字段,并用空格分隔。

    然后我们只需执行相同的命令,但使用最后一个字段而不是第一个字段。然后,这将在下一行打印出相应的信息,每条记录再次用空格分隔。

    如果您希望新列的宽度固定,我建议使用printf,但我相信还有其他方法可以做到这一点(我听说 Perl 擅长此操作)。

    printf 禁止换行,因此我们可以删除 awk 的 ORS 部分。 但是,由于永远不会创建换行符,我们必须在每个 awk 命令之后附加一个带有单独 printf 命令的 end 语句,以使我们进入一个新行,即,

    awk '{printf "%10s",$1}END{printf"\n"}' file.txt
    awk '{printf "%10s",$NF}END{printf"\n"}' file.txt
    

    这将为使用 awk 语句找到的所有内容打印十个字符长的列。

    输出:

    joined  whatever       last    useful  notuseful     email randomwat
         a         b          c         y         f          z        g
    

    注意:awk 根据空格确定每一列的内容。因此,在您的示例中,“上次访问”将仅打印最后一次,因为那是第一列。如果您希望将多个单词分组到一个特定的列中,请将这组单词括在引号 "" 中。

    【讨论】:

    • 更新,如果您需要它来执行相同的想法,但针对不同的记录集(看起来您的示例中有三个),请使用 grep 或 sed 命令单独获取每条记录,然后传递它一直到 awk。
    【解决方案3】:

    我不认为 awk 是完成这项工作的最佳工具,或者我只是不知道 awk 的某些方面会使这变得更容易。

    awk 'BEGIN{split("4,6,1,3",x,",");i=1;FS=":"}
        function s(e){gsub(/( *$)|(^ *)/,"",e);return e}
        function p(a){for(j=1;j<length(x);++j)printf "%s,",a[x[j]];print a[x[j]]}
        function e(){if(!d){p(h);d=1}p(v);i=1}
        {if(NF==1)e();else{if(!d)h[i]=s($1);v[i++]=s($2)}}
        END{e()}' infile.txt
    

    这不是太优雅,但这个碍眼的工作。您还可以修改 "4,6,1,3" 以选择以 csv 格式打印的字段。
    对于您的示例,这将输出以下内容:

    Useful,Email,Joined,Last visited
    y,z,a,c
    y,z,a,c
    k,m,h,j
    

    此命令要求所有条目具有相同的字段,键和值以: 分隔,条目之间由任何不包含冒号的行分隔。

    我知道我迟到了,但我很想听听你是否想出了更好的解决方案来解决这个问题。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-07-06
      • 1970-01-01
      • 2015-02-24
      • 2015-07-11
      • 1970-01-01
      • 2012-04-30
      • 2017-05-14
      相关资源
      最近更新 更多