【问题标题】:How can I adjust the length of a column field in bash using awk or sed?如何使用 awk 或 sed 在 bash 中调整列字段的长度?
【发布时间】:2016-12-11 10:01:47
【问题描述】:

我有一个 input.csv 文件,其中第 2 列和第 3 列的长度可变。

100,Short Column, 199
200,Meeedium Column,1254
300,Loooooooooooong Column,35

我正在尝试使用以下命令来实现干净的制表,但我需要用一定数量的空格填充第二列以获得固定长度的列(假设总长度为 30就够了)。

awk -F, '{print $1 "\t" $2 "\t" $3;}' input.csv

我当前的输出如下所示:

100   Short Column   199
200   Meeedium Column   1254
300   Loooooooooooong Column   35

我想通过正确填写第二列和第三列来实现以下输出:

100   Short Column               199
200   Meeedium Column           1254
300   Loooooooooooong Column      35

应该使用关于 awk 或 sed 命令的任何好主意吗? 谢谢大家。

【问题讨论】:

  • column -t input.csv 或查看printfawkprintf
  • column -s, -t file 应该这样做
  • @anubhava:谢谢。
  • @fedorqui FIELDWIDTHS 用于阅读,而不是用于写作,它对此无济于事。
  • @EdMorton 不错。注意到了,谢谢。

标签: linux bash csv awk sed


【解决方案1】:

awk 中使用printf

$ awk -F, '{gsub(/ /, "", $3); printf "%-5s %-25s%5s\n", $1, $2, $3}' file input.csv
100   Short Column               199
200   Meeedium Column           1254
300   Loooooooooooong Column      35

我上面所做的是将IFS,字段分隔符设置为,;由于该文件仅在第 3 列中有一些空格,因此它会破坏 printf 如何处理字符串,使用 gsub 删除它并以 C 样式 printf 格式化。

【讨论】:

  • 或使用 bash 的 printf:while IFS=, read -r a b c; do printf "%5s %-25s%5s\n" "$a" "$b" "$c"; done < file
  • @Inian 好代码!谢谢你。有什么方法可以自定义 awk 以便开始仅针对某些特定列进行对齐?例如:在具有 30 列的 csv 中,我希望仅将对齐应用于第 20、21、22 列(因为第 1 到 19 和 23 到 30 列只是我希望从打印中丢弃的页眉/页脚)。
【解决方案2】:

使用perl的解决方案

$ perl -pe 's/([^,]+),([^,]+),([^,]+)/sprintf "%-6s%-30s%5s", $1,$2,$3/e' input.csv
100   Short Column                   199
200   Meeedium Column               1254
300   Loooooooooooong Column          35

【讨论】:

    【解决方案3】:

    不要选择任意数字作为每个字段的宽度,而是执行 2 遍方法,其中第一遍计算每个字段的最大长度,第二遍打印宽度为该大小加上几个空格的字段字段:

    $ cat tst.awk
    BEGIN { FS=" *, *"; OFS="  " }
    NR==FNR {
        for (i=1;i<=NF;i++) {
            w[i] = (length($i) > w[i] ? length($i) : w[i])
            if ($i ~ /[^0-9]/) {
                a[i] = "-"
            }
        }
        next
    }
    {
        for (i=1;i<=NF;i++) {
            printf "%"a[i]w[i]"s%s", $i, (i<NF ? OFS : ORS)
        }
    }
    
    $ awk -f tst.awk file file
    100  Short Column             199
    200  Meeedium Column         1254
    300  Loooooooooooong Column    35
    

    上面还对非数字字段使用左对齐,对所有数字字段使用右对齐。无论输入字段有多长,无论您有多少字段,它都会起作用:

    $ cat file1
    100000,Short Column, 199,a
    100,Now is the Winter of our discontent with fixed width fields,20000,b
    100,Short Column, 199,c
    200,Meeedium Column,1254,d
    300,Loooooooooooong Column,35,e
    
    $ awk -f tst.awk file1 file1
    100000  Short Column                                                   199  a
       100  Now is the Winter of our discontent with fixed width fields  20000  b
       100  Short Column                                                   199  c
       200  Meeedium Column                                               1254  d
       300  Loooooooooooong Column                                          35  e
    

    【讨论】:

    • 出色的解决方案。是否有任何方法可以自定义 tst.awk(或 bash 中的命令行)以便开始仅针对某些特定列进行对齐?例如:在具有 30 列的 csv 中,我希望仅将对齐应用于第 20、21、22 列(因为从 1 到 19 的列只是我希望从打印中丢弃的标题)。
    • 当然,只需将1 更改为您的起始字段编号,将NF 更改为您的结束字段编号。如果您愿意,将这些开始/结束值与使用-v 设置的变量一起传递。如果您无法弄清楚,请尝试并发布一个新的后续问题。但是,如果您得到了这个问题的答案,那么请记住通过单击它旁边的复选标记来接受您选择的任何一个(请参阅stackoverflow.com/help/someone-answers)。
    • 感谢您的提示,脚本运行良好!我想知道是否有任何方法可以修改 tst.awk 脚本以使数字字段正确对齐,也可以在某些小数的情况下,目前,脚本在右侧对齐数量 1000,但在左侧对齐数量 1000.99(实际上这是我的错误,我没有在我的问题中指定数字字段包含小数)。可能我应该在您的行中添加一些代码,以以下开头:if ($i ~ /[^0-9]/)。再次感谢您,很棒的代码!
    • 当然,只要加一个点[^0-9.]。不客气。
    猜你喜欢
    • 2023-01-25
    • 1970-01-01
    • 2020-06-22
    • 2019-02-20
    • 1970-01-01
    • 2011-06-06
    • 2018-05-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多