代码之家  ›  专栏  ›  技术社区  ›  Bluz

使用awk-sed和grep将多行文本转换为csv

  •  0
  • Bluz  · 技术社区  · 3 年前

    我运行了一个shell命令,它返回一个重复值列表,如下所示(注意缩进):

    Name:               vm346
      cpu                1 (12%)      6150m (76%)
      memory             1130Mi (7%)  1130Mi (7%)
    Name:               vm847
      cpu                6 (75%)        30150m (376%)
      memory             12980Mi (87%)  12980Mi (87%)
    Name:               vm848
      cpu                3500m (43%)   17150m (214%)
      memory             6216Mi (41%)  6216Mi (41%)
    

    我正试图像这样转换数据(csv):

    vm346,1,(12%),6150m,(76%),1130Mi,(7%),1130Mi,(7%)
    vm847,6,(75%),30150m,(376%),12980Mi,(87%),12980Mi,(87%)
    vm848,3500m,(43%),17150m,(214%),6216Mi,(41%),6216Mi,(41%)
    

    问题是,任何给定的数据集(如上面的数据集)总是在多行上。

    当我把它管到里面时,它会让我发疯,因为即使我使用:

    BEGIN{ FS="\n" }
    

    试图将数据缝合在一行中,这是行不通的。无论我做什么,awk都会将名称值作为一行分隔开。

    很抱歉,我没有太多的代码可以分享,但我已经用它转了几个小时了,我的想法快用完了。。。

    1 回复  |  直到 3 年前
        1
  •  4
  •   choroba    3 年前

    我可以用Perl解决这个问题:

    perl -ane 'print join ",", @F[1 .. $#F]; print $F[0] eq "memory" ? "\n" : ","'
    

    如果你需要的话,把它翻译成awk应该很容易。

    它是如何工作的?

    • -a 将空白处的每一行拆分为@F数组
    • -n 逐行读取输入并运行之后指定的代码 -e 对于每条线路
    • 我们打印除第一个元素外的所有元素(请参见 join )
    • 然后我们看第一列,如果是内存,我们在块的最后一行,所以我们打印一条换行符,否则我们打印一个逗号
        2
  •  3
  •   jared_mamrot    3 年前

    对于AWK,一个选项是将RS设置为“Name:”,并使用忽略第一条记录 NR > 1 ,例如。

    awk -v RS="Name: " 'BEGIN{OFS=","} NR > 1 {print $1, $3, $4, $5, $6, $8, $9, $10, $11}' file
    #> vm346,1,(12%),6150m,(76%),1130Mi,(7%),1130Mi,(7%)
    #> vm847,6,(75%),30150m,(376%),12980Mi,(87%),12980Mi,(87%)
    #> vm848,3500m,(43%),17150m,(214%),6216Mi,(41%),6216Mi,(41%)
    
        3
  •  2
  •   user14473238 user14473238    3 年前
    awk '{$1=""}1' | paste -sd'  \n' - | awk '{$1=$1}1' OFS=,
    

    去掉第一列。每隔三行连接一次。与sed的想法相同:

    sed 's/^ *[^ ]* *//' | paste -sd'  \n' - | sed 's/  */,/g'
    

    其他内容:

    awk '
    $1=="Name:" {
      sep=ors
      ors=ORS
    } {
      for (i=2;i<=NF;++i) {
        printf "%s%s",sep,$i
        sep=OFS
      }
    } END {printf "%s",ors}'
    

    或者,如果您想根据第一个字段“内存”打印ORS(请注意,此程序可能在不打印终止ORS的情况下结束):

    awk '{for (i=2;i<=NF;++i) printf "%s%s",$i,(i==NF && $1=="memory" ? ORS : OFS)}'
    

    其他内容:

    awk -v OFS=, '
    index($0,$1)==1 {
      OFS=ors
      ors=ORS
    } {
      $1=""
      printf "%s",$0
      OFS=ofs
    } END {printf "%s",ors} BEGIN {ofs=OFS}'
    
        4
  •  1
  •   potong    3 年前

    这可能适用于您(GNU sed):

    sed -nE '/^ +\S+ +/{s///;H;$!d};x;/./s/\s+/,/gp;x;s/^\S+ +//;h' file
    

    总的来说,sed程序处理缩进的行、已聚集的行(除非当前行是文件的第一行)和非缩进的行。

    关闭隐式打印并启用扩展regexp( -nE ).

    如果当前行缩进,请删除缩进、第一个字段和后面的任何空格,将结果追加到保留空格,如果不是最后一行,则将其删除。

    否则,请检查收集行的保留空间,如果找到,请用逗号替换一个或多个空白并打印结果。然后通过删除第一个字段和任何后续空格来准备当前行,并用结果替换保留空格。

    从逻辑上讲,这个解决方案似乎是前后颠倒的,但这种风格的编程避免了多次检查文件结尾以及调用标签和gotos。

    注意:此解决方案适用于任意数量的缩进行。

        5
  •  1
  •   dawg    3 年前

    这里有一个红宝石可以做到这一点:

    ruby -e '
    s=$<.read
    s.scan(/^([^ \t]+:)([\s\S]+?)(?=^\1|\z)/m).      # parse blocks
        map(&:last).                                 # get data part
        # parse and join the data fields:
        map{|block| block.split(/\n[ \t]+[^ \t]+[ \t]+/)}.
        map{|lines| lines.map(&:strip).join(" ").split().join(",")}.
        each{|l| puts "#{l}"}
    ' file 
    vm346,1,(12%),6150m,(76%),1130Mi,(7%),1130Mi,(7%)
    vm847,6,(75%),30150m,(376%),12980Mi,(87%),12980Mi,(87%)
    vm848,3500m,(43%),17150m,(214%),6216Mi,(41%),6216Mi,(41%)
    

    优点是这不取决于行的数量或字段的数量。它正在解析以下形式的块中的数据:

    START:   ([ \t]+[data_with_no_space])*\n
       l1    ([ \t]+[data_with_no_space])*\n
       ...
    START:
       ...
    

    工作方式如下:

    1. 使用解析块 THIS REGEX ;
    2. 保存数据元素的数组;
    3. 连接子数组,然后拆分为数据字段;
    4. 加入(',')以生成csv。