UNIX:从分隔的文本文件创建表格格式的输出

时间:2020-05-06 06:10:40

标签: shell unix awk

我需要从文本文件中获取表格格式的输出,并且要通过以下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

如果您可以看到Description列的输出,则它不会在其自己的列中格式化输出,而是由于字符串长度而使整个表弄乱了。

有人可以查看并建议我如何更好地显示“描述”列的输出吗?

4 个答案:

答案 0 :(得分:4)

我会使用Term::Table模块(可通过操作系统的程序包管理器安装或通过CPAN进行安装)在perl中完成此操作,该模块将自动计算出列宽并根据需要包装文本:

import pygame as pg
import cv2
import random
pg.init()


display_width = 1000
display_height = 1000

BLUE = 47, 158, 189
RED = 255, 0, 0

display = pg.display.set_mode((display_width, display_height))
pg.display.set_caption("Cars Evolution")

icon = pg.image.load("icon.png")
pg.display.set_icon(icon)

clock = pg.time.Clock()

car_url = "car_img.png"
car_direction = 0  # car_direction from -1 to 1


beam_url = "beam_img.png"
beam_x = 200
beam_y = 200
beam_width = 20
beam_height = 160

total_level_width = 5000
total_level_height = 5000


class Beam(pg.sprite.Sprite):
    def __init__(self, input_beam_url, input_beam_x, input_beam_y):
        pg.sprite.Sprite.__init__(self)
        self.x = input_beam_x
        self.y = input_beam_y
        self.image = pg.image.load(input_beam_url)
        self.width = 20
        self.height = 160
        self.rect = self.image.get_rect()

    def draw_beam(self):
        self.x -= car.delta_x
        self.y -= car.delta_y
        display.blit(self.image, (self.x, self.y))
        pg.draw.rect(display, RED, pg.Rect(self.x, self.y, self.width, self.height), 2)


class Car(pg.sprite.Sprite):
    def __init__(self, input_car_url):
        pg.sprite.Sprite.__init__(self)
        self.image = pg.image.load(input_car_url)
        self.x = display_width / 2
        self.y = display_height / 3
        self.width = 46
        self.height = 89
        self.rect = self.image.get_rect()
        self.delta_x = 0
        self.delta_y = 0

    def draw_car(self):
        display.blit(self.image, (self.x, self.y))
        pg.draw.rect(display, RED, pg.Rect(self.x, self.y, self.width, self.height), 2)

    def move_car(self, vertical_ind, horizontal_ind, speed):
        self.delta_x = horizontal_ind * speed
        self.delta_y = vertical_ind * speed

    def is_collision(self, input_beams_list):
        collide_beam_list = pg.sprite.spritecollide(self, input_beams_list, False)
        if collide_beam_list:
            print("collided")
            return True


def close_game():
    game = False
    pg.quit()
    quit()


beams_amount = 5
beams_list = pg.sprite.Group()

random.seed(57)

for beam in range(beams_amount):
    beam_to_add = Beam(beam_url, random.randint(beam_width, display_width - beam_width), random.randint(beam_height, display_height - beam_height))
    beams_list.add(beam_to_add)


car = Car(car_url)

def run_game():
    game = True

    while game:
        for event in pg.event.get():
            if event.type == pg.QUIT:
                close_game()

        display.fill(BLUE)

        # Drawing

        beams_list.update()

        car.draw_car()
        car.move_car(0.5, 0, 5)

        for sprite_object in beams_list.sprites():
            sprite_object.draw_beam()

        car.is_collision(beams_list)
            #close_game()

        pg.display.update()

        clock.tick(60)


run_game() 

用法示例:

function findSchedules(name, jsonNode)
{
  if (hasChildren(jsonNode) == false)
  {
    data = csvToJSON(jsonNode['?value']);
    dataPath = data['dataPath'];
    schedulePoint = data['schedulePoint'];

    if (schedulePoint != "" && schedulePoint != null && schedulePoint != "NO SCHEDULE")
      return [[name, dataPath, csvToJSON(jsonNode['?value'])['schedulePoint']]];
    else
      return [];
  }

  schedules = [];

  for (node in jsonNode)
  {
    if (isValidNode(node) == true)
      schedules = schedules.concat(findSchedules(node, jsonNode[node]));
      /*
          If instead I wrote:

          if (isValidNode(node) == true)
          {
            prevSchedules = findSchedules(node, jsonNode[node]);
            schedules = schedules.concat(prevSchedules);
          }

          which is what I had originally, the recursion would not work correctly. Why are these two 
          statements different?
      */
  }

  data = csvToJSON(jsonNode['?value']);
  dataPath = data['dataPath'];
  schedulePoint = data['schedulePoint'];

  if (schedulePoint != "" && schedulePoint != null && schedulePoint != "NO SCHEDULE")
    schedules.push([name, dataPath, schedulePoint]);

  return schedules;
}

请考虑一下,由于perl formats,它也可以在没有任何非核心模块的情况下完成。我实际上更喜欢这种方式,因为它可以更好地自动换行(尽管更改表或单个列的整体宽度变得更加麻烦):

#!/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                  |
+--------+--------------------------+-----------+--------------------------+

答案 1 :(得分:4)

我让UNIX实用程序fold对要包装的字段进行换行,因为它知道尝试在空格处分割等,以使包装的文本尽可能地可读:

$ 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 :(得分:2)

编辑: 使用标题尝试以下操作。

error


由于OP尚未提及在字段中添加空格的逻辑,但是从输出中看可以说它可能基于字段中的最大长度值,因此,基于此假设,您可以尝试以下方法(根据显示的示例。

subscribe(success, error)

以上解决方案的说明: 添加了以上详细说明。

app.ts

答案 3 :(得分:1)

这是另一个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 |               
相关问题