【问题标题】:csv2md format output using awk使用 awk 的 csv2md 格式输出
【发布时间】:2021-08-11 15:34:59
【问题描述】:

我需要使用 awk 创建一个降价格式的输出,这类似于我们使用 mysql 获得的表结果。换句话说,我试图模仿https://www.convertcsv.com/csv-to-markdown.htm 使用以下输入。

id|type|cost|date|ship
0|A|223|201603|PORT
0|A|22|201602|PORT
0|A|422|201601|DOCK
1|B|3213|201602|DOCK
1|B|3213|201601|PORT
2|C|2321|201601|DOCK

我正在寻找的输出如下

------------------------------
|id |type |cost |  date |ship |
------------------------------
|0  |A    |223  |201603 |PORT |
|0  |A    |22   |201602 |PORT |
|0  |A    |422  |201601 |DOCK |
|1  |B    |3213 |201602 |DOCK |
|1  |B    |3213 |201601 |PORT |
|2  |C    |2321 |201601 |DOCK |
------------------------------

我的第一次尝试是获取每列的最大大小,并在打印时在格式中使用它。但是下面的没有按预期工作。

awk -F"|" ' 
NR==1 { hdr=$0; for(i=1;i<=NF;i++) { a[i]=length($i) } next } 
{ for(i=1;i<=NF;i++) { ;a[i]=length($i)>a[i]?length($i):a[i] } content[NR]=$0 } 
END { 
for(i in a) len+=a[i]+2;
$0=""; OFS="-"; len++; NF=len; print ;
n=split(hdr,arr,FS); 
for(i=1;i<=n;i++) 
  {  printf("%6s |",arr[i]); }  # instead of 6 i want to pass a[i] ==> "%6" a[i] "s |" is not working
print "";

} 
' data.txt

如何修复它并获得所需的输出。

【问题讨论】:

    标签: shell awk


    【解决方案1】:

    这需要 2-pass 方法,以下任一方法都可以:

    A) 两次读取输入文件,因此它使用的内存非常少:

    $ cat tst.awk
    BEGIN { FS=OFS="|" }
    NR==FNR {
        for (i=1; i<=NF; i++) {
            wid = length($i)
            wids[i] = (wid > wids[i] ? wid : wids[i])
        }
        next
    }
    FNR==1 {
        totWid = NF+1
        for (i=1; i<=NF; i++) {
            totWid += wids[i]
        }
        dashes = sprintf("%*s",totWid,"")
        gsub(/ /,"-",dashes)
        print dashes
        printf "%s", OFS
        for (i=1; i<=NF; i++) {
            printf "%*s%s", wids[i], $i, OFS
        }
        print ""
        print dashes
        next
    }
    {
        printf "%s", OFS
        for (i=1; i<=NF; i++) {
            printf "%-*s%s", wids[i], $i, OFS
        }
        print ""
    }
    END { print dashes }
    

    $ awk -f tst.awk file file
    --------------------------
    |id|type|cost|  date|ship|
    --------------------------
    |0 |A   |223 |201603|PORT|
    |0 |A   |22  |201602|PORT|
    |0 |A   |422 |201601|DOCK|
    |1 |B   |3213|201602|DOCK|
    |1 |B   |3213|201601|PORT|
    |2 |C   |2321|201601|DOCK|
    --------------------------
    

    B) 将整个文件存储在内存中,然后第二遍是数组遍历,而不是再次读取文件,如果你有足够的内存来执行它应该运行得更快:

    $ cat tst.awk
    BEGIN { FS=OFS="|" }
    {
        for (i=1; i<=NF; i++) {
            wid = length($i)
            wids[i] = (wid > wids[i] ? wid : wids[i])
            vals[NR,i] = $i
        }
    }
    END {
        totWid = NF+1
        for (i=1; i<=NF; i++) {
            totWid += wids[i]
        }
        dashes = sprintf("%*s",totWid,"")
        gsub(/ /,"-",dashes)
        print dashes
        printf "%s", OFS
        for (i=1; i<=NF; i++) {
            printf "%*s%s", wids[i], vals[1,i], OFS
        }
        print ""
        print dashes
    
        for (lineNr=2; lineNr<=NR; lineNr++) {
            printf "%s", OFS
            for (i=1; i<=NF; i++) {
                printf "%-*s%s", wids[i], vals[lineNr,i], OFS
            }
            print ""
        }
        print dashes
    }
    

    $ awk -f tst.awk file
    --------------------------
    |id|type|cost|  date|ship|
    --------------------------
    |0 |A   |223 |201603|PORT|
    |0 |A   |22  |201602|PORT|
    |0 |A   |422 |201601|DOCK|
    |1 |B   |3213|201602|DOCK|
    |1 |B   |3213|201601|PORT|
    |2 |C   |2321|201601|DOCK|
    --------------------------
    

    【讨论】:

    • 感谢 Ed,它有效.. 在代码 printf "%-*s%s", wids[i], vals[lineNr,i], OFS 为什么缺少 1%
    • 请注意%-*s 中的* - 这是您将字段宽度指定为参数之一的地方,在本例中为wids[i]printf "%*s\n", 10, "foo" 在功能上等同于 printf "%"10"s\n", "foo"。请参阅gnu.org/software/gawk/manual/gawk.html#Format-Modifiers 上的“动态宽度和精确度能力”。
    【解决方案2】:

    有一个用于制作表格输出的程序,即columns -s '|' -t,但它在其输出中丢失了分隔符|

    您可以使用sed 恢复该分隔符,将每个空格后跟一个非白色字符替换为| 后跟该非白色字符。

    最后,awk 帮助您在列标题前后添加一行。

    column -s '|' -t input.csv | sed -E 's/ ([^ ])/|\1/g' | awk '(NR == 1) {l = $0; gsub(/./, "-", l); print l "\n" $0 "\n" l} (NR > 1) {print} END{print l}'
    

    【讨论】:

    • 与单行的良好近似,我可以接受。顺便说一句,列命令有一些限制 wrt 空白.. 对吗?....即解析不好..
    • @stack0114106:columns在分隔符为|时没有问题。如果字段在sed 命令级别包含空白,则问题可能会出现。
    【解决方案3】:

    这是我使用 Ed 的概念的答案。

    awk -F"|" ' 
    NR==1 { hdr=$0; for(i=1;i<=NF;i++) { a[i]=length($i) } next } 
    { 
      for(i=1;i<=NF;i++) a[i]=length($i)>a[i]?length($i):a[i] ;
      content[NR]=$0 
    } 
    END { 
    dashes="+";
      for(i in a) 
      {      len+=a[i]+1; 
             dash=sprintf("%*s",a[i],""); 
             gsub(/ /,"-",dash); 
             dashes=dashes dash "+" 
      }
     #Header
        print dashes;
        n=split(hdr,arr,FS); printf("|");
        for(i=1;i<=n;i++) { fmt="%" a[i] "s|" ; printf(fmt,arr[i]); } print "";
        print dashes;
    
     #Contents  
        for(i=2;i<=NR;i++) 
        {
         n=split(content[i],arr,FS); printf("|");
         for(j=1;j<=n;j++) { fmt="%-" a[j] "s|" ; printf(fmt,arr[j]); } print "";
        }
        print dashes
    } 
    ' data.txt
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-04-15
      • 1970-01-01
      • 2016-12-23
      • 2010-12-23
      相关资源
      最近更新 更多