UNIX: Создание вывода в формате таблицы из текстового файла с разделителями - PullRequest
3 голосов
/ 06 мая 2020

У меня есть требование получить вывод формата таблицы из текстового файла, и я добиваюсь этого с помощью следующей команды awk.

Файл с разделителями

ACTIVE#1238917238971238#USA#The U.S. is a country of 50 states covering a vast swath of North America.
ACTIVE#21389721839781237812#INDIA#India, officially the Republic of India, is a country in South Asia.
ACTIVE#3121278372183782137812#AUSTRALIA#Australia, officially the Commonwealth of Australia, is a sovereign country comprising the mainland of the Australian continent

Команда AWK

awk -F"#" 'BEGIN {{printf "%-80s\n","--------------------------------------------------------------------------------------------------------------------------------------"} {printf "|%-12s|%-30s|%-38s|%-50s|\n","STATUS","ID", "Country", "Description"} {printf "%-80s\n","--------------------------------------------------------------------------------------------------------------------------------------"}} {printf "|%-12s|%-30s|%-38s|%-50s|\n",$1,$2,$3,$4} END{printf "%-80s\n","--------------------------------------------------------------------------------------------------------------------------------------"}' /tmp/test.txt

Вывод: enter image description here

Если вы видите вывод для столбца описания, он не форматирует вывод в собственном столбце, а испортил всю таблицу из-за до длины строки.

Может ли кто-нибудь просмотреть и предложить мне, как я могу лучше отобразить вывод для столбца Описание?

Ответы [ 4 ]

4 голосов
/ 06 мая 2020

Я бы сделал это в perl вместо этого, используя модуль Term :: Table (устанавливается через менеджер пакетов вашей ОС или вне CPAN), который автоматически определяет ширину столбцов и переносит текст. по мере необходимости:

#!/usr/bin/env perl
use strict;
use warnings;
use feature qw/say/;
use Term::Table;

my @lines = map { chomp; [ split /#/ ] } <>;
say for Term::Table->new(
    max_width => 80,
    header => ["Status", "ID", "Country", "Description"],
    rows => \@lines
    )->render;

Пример использования:

$ ./table.pl < input.txt
+--------+--------------------------+-----------+--------------------------+
| Status | ID                       | Country   | Description              |
+--------+--------------------------+-----------+--------------------------+
| ACTIVE | 1238917238971238         | USA       | The U.S. is a country of |
|        |                          |           |  50 states covering a va |
|        |                          |           | st swath of North Americ |
|        |                          |           | a.                       |
|        |                          |           |                          |
| ACTIVE | 21389721839781237812     | INDIA     | India, officially the Re |
|        |                          |           | public of India, is a co |
|        |                          |           | untry in South Asia.     |
|        |                          |           |                          |
| ACTIVE | 3121278372183782137812   | AUSTRALIA | Australia, officially th |
|        |                          |           | e Commonwealth of Austra |
|        |                          |           | lia, is a sovereign coun |
|        |                          |           | try comprising the mainl |
|        |                          |           | and of the Australian co |
|        |                          |           | ntinent                  |
+--------+--------------------------+-----------+--------------------------+

Если подумать, это можно сделать и без каких-либо неосновных модулей, благодаря perl форматы . На самом деле мне этот способ нравится больше, потому что он лучше переносит слова (хотя становится более громоздко изменять общую ширину таблицы или даже отдельных столбцов):

#!/usr/bin/env perl
use strict;
use warnings;
use feature qw/say/;

my ($status, $id, $country, $description);
while (<>) {
    chomp;
    ($status, $id, $country, $description) = split /#/;
    write;
}
say "+--------+------------------------+-----------+-------------------------------+";

format STDOUT_TOP =
+--------+------------------------+-----------+-------------------------------+
| Status | Id                     | Country   | Description                   |
+--------+------------------------+-----------+-------------------------------+
.

format STDOUT =
| @<<<<< | @<<<<<<<<<<<<<<<<<<<<< | @<<<<<<<< | ^<<<<<<<<<<<<<<<<<<<<<<<<<<<< |
  $status, $id,                     $country,   $description
|~~      |                        |           | ^<<<<<<<<<<<<<<<<<<<<<<<<<<<< |
                                                $description
|        |                        |           |                               |
.
$ ./table.pl < input.txt
+--------+------------------------+-----------+-------------------------------+
| Status | Id                     | Country   | Description                   |
+--------+------------------------+-----------+-------------------------------+
| ACTIVE | 1238917238971238       | USA       | The U.S. is a country of 50   |
|        |                        |           | states covering a vast swath  |
|        |                        |           | of North America.             |
|        |                        |           |                               |
| ACTIVE | 21389721839781237812   | INDIA     | India, officially the         |
|        |                        |           | Republic of India, is a       |
|        |                        |           | country in South Asia.        |
|        |                        |           |                               |
| ACTIVE | 3121278372183782137812 | AUSTRALIA | Australia, officially the     |
|        |                        |           | Commonwealth of Australia, is |
|        |                        |           | a sovereign country           |
|        |                        |           | comprising the mainland of    |
|        |                        |           | the Australian continent      |
|        |                        |           |                               |
+--------+------------------------+-----------+-------------------------------+
4 голосов
/ 06 мая 2020

Я бы позволил утилите UNIX fold выполнить перенос строк для полей, которые вы хотите обернуть, так как она знает, что нужно попытаться разбить на пробелы, et c. чтобы обернутый текст оставался как можно более читаемым:

$ cat tst.awk
BEGIN {
    FS = "#"
    OFS = "|"
}
NR == 1 {
    split("8 12 10 45",fldWidths," ")
    rowWidth = NF + 1   # for the OFSs between fields and at the start/end of the line
    for (i in fldWidths) {
        rowWidth += fldWidths[i]
    }

    rowSep = sprintf("%*s",rowWidth,"")
    gsub(/ /,"-",rowSep)

    print rowSep
    split("STATUS ID Country Description",hdrs," ")
    for (i=1; i<=NF; i++) {
        printf "%s%-*s", OFS, fldWidths[i], hdrs[i]
    }
    print OFS
    print rowSep
}
{
    numRows = 0
    for (fldNr=1; fldNr<=NF; fldNr++) {
        cmd = "printf \047%s\n\047 \047" $fldNr "\047 | fold -s -w " fldWidths[fldNr]
        rowNr = 0
        while ( (cmd | getline line) > 0 ) {
            rows[++rowNr,fldNr] = line
            numRows = (numRows > rowNr ? numRows : rowNr)
        }
        close(cmd)
    }
    for (rowNr=1; rowNr<=numRows; rowNr++) {
        for (fldNr=1; fldNr<=NF; fldNr++) {
            printf "%s%-*s", OFS, fldWidths[fldNr], rows[rowNr,fldNr]
        }
        print OFS
    }
    print rowSep
}

.

$ awk -f tst.awk file
--------------------------------------------------------------------------------
|STATUS  |ID          |Country   |Description                                  |
--------------------------------------------------------------------------------
|ACTIVE  |123891723897|USA       |The U.S. is a country of 50 states covering  |
|        |1238        |          |a vast swath of North America.               |
--------------------------------------------------------------------------------
|ACTIVE  |213897218397|INDIA     |India, officially the Republic of India, is  |
|        |81237812    |          |a country in South Asia.                     |
--------------------------------------------------------------------------------
|ACTIVE  |312127837218|AUSTRALIA |Australia, officially the Commonwealth of    |
|        |3782137812  |          |Australia, is a sovereign country comprising |
|        |            |          |the mainland of the Australian continent     |
--------------------------------------------------------------------------------

Массажируйте ширину поля, чтобы она соответствовала.

2 голосов
/ 06 мая 2020

РЕДАКТИРОВАТЬ: С заголовками попробуйте следующее.

awk -v line="-----------------------------------" '
BEGIN{
  FS="#"
  OFS="|"
  num=split("STATUS,ID,Country,Description",a,",")
  print line
}
FNR==NR{
  for(i=2;i<=NF;i++){
    max[i]=max[i]>=length($i)?max[i]:length($i)
  }
  next
}
FNR==1{
  for(i=1;i<=num;i++){
    header=(header?header OFS:"")sprintf("%-"max[i]"s",a[i])
  }
  print header
}
{
  for(i=1;i<=NF;i++){
    $i=sprintf("%-"max[i]"s",$i)
  }
}
1;
END{
  print line
}
'  Input_file  Input_file


Поскольку OP не упомянул logi c о добавлении пробелов в полях, но, глядя на результат, можно сказать, что он может быть основан на значении максимальной длины в поле, поэтому, исходя из этого предположения, не могли бы вы попробовать следующее (проверено и написано на основе показанных образцов).

awk '
BEGIN{
  FS="#"
  OFS="|"
}
FNR==NR{
  for(i=2;i<=NF;i++){
    max[i]=max[i]>=length($i)?max[i]:length($i)
  }
  next
}
{
  for(i=1;i<=NF;i++){
    $i=sprintf("%-"max[i]"s",$i)
  }
}
1
'  Input_file Input_file

Объяснение приведенного выше решения: Добавление подробного объяснения для приведенного выше.

awk '                                                ##Starting awk program from here.
BEGIN{                                               ##Starting BEGIN section of this program from here.
  FS="#"                                             ##Setting OFS as | here for all lines.
  OFS="|"
}
FNR==NR{                                             ##Checking condition FNR==NR which will be TRUE when first Input_file is being read here.
  for(i=2;i<=NF;i++){                                ##Running for loop from 2nd field to last field of lines.
    max[i]=max[i]>=length($i)?max[i]:length($i)      ##Creating array max with index and value of either current field length OR max array value.
  }
  next                                               ##next will skip all further statements from here.
}
{
  for(i=1;i<=NF;i++){                                ##Running for loop from 1st field to last field of lines.
    $i=sprintf("%-"max[i]"s",$i)                     ##Re-creating first field with sprintf and adding spaces after each field value.
  }
}
1                                                    ##Mentioning 1 will print current line here.
' Input_file Input_file                              ##Mentioning Input_file names here.
1 голос
/ 06 мая 2020

Вот еще один awk. Он вычисляет среднюю длину полей, а затем долю терминала, используемую для вывода. Вероятно, есть подходы лучше, чем средний (или максимальный), но я пробовал только те 2. Он использует tput cols для получения ширины терминала:

$ awk '
BEGIN {
    FS="#"                                             # delims
    OFS=""                                             # to allow length==0
}
NR==FNR {                                              # avg field lenghts *
    for(i=1;i<=NF;i++)
        avg[i]+=length($i)
    next
}
FNR==1 {
    if(("tput cols"|getline cols)<0 || cols<2*NF-1) {  # get terminal width
        print "Yours is too small"                     # exit if too small
        exit                                           # in reality fails when
    }                                                  # field width rounds to 0
    for(i in avg) {         
        avg[i]=avg[i]/(NR-1)                           # * avg divided here
        avgs+=avg[i]
    }
    for(i=1;i<=NF;i++)                                 # below: field terminal 
        size[i]=((v=sprintf("%0.f",((avg[i]/avgs)*cols)-1))>0?v:1) # proportions
}                                                      # rounded with %0.f, min 1
{
    while(length>0)                                    # while unprinted chars
    for(i=1;i<=NF;i++) {                               # keep outputing
        printf "%-" size[i] "s%s",substr($i,1,size[i]),(i==NF?ORS:"|")
        $i=substr($i,size[i]+1)                        # cut printed from fields
    }
}' file file                                           # 2 runs

Вывод для 64-разрядного терминала:

AC|123891723|US|The U.S. is a country of 50 states covering a v
TI|8971238  |A |ast swath of North America.                    
VE|         |  |                                               
AC|213897218|IN|India, officially the Republic of India, is a c
TI|397812378|DI|ountry in South Asia.                          
VE|12       |A |                                               
AC|312127837|AU|Australia, officially the Commonwealth of Austr
TI|218378213|ST|alia, is a sovereign country comprising the mai
VE|7812     |RA|nland of the Australian continent              
  |         |LI|                                               
  |         |A |               
...