Search code examples
bashsh

Print every few lines as a column


I have a script that gets information about servers and stores it in a variable.

➜ echo $report
 Hostname : or-05-arm
 CheckDate: 11:47 27-10-2022
 CPU Usage: [..........] 0%  [0.00/2]
 RAM Usage: [###.......] 31% [1.8G/5.8G]
 HDD Avail: 26G
 Uptime   : up 19 weeks

 Hostname : contabo-3
 CheckDate: 14:47 27-10-2022
 CPU Usage: [#####.....] 50% [3.01/6]
 RAM Usage: [###.......] 30% [4.7G/15.6G]
 HDD Avail: 136G
 Uptime   : up 1 week

 Hostname : contabo-2
 CheckDate: 14:47 27-10-2022
 CPU Usage: [#####.....] 53% [3.16/6]
 RAM Usage: [##........] 25% [3.9G/15.6G]
 HDD Avail: 176G
 Uptime   : up 1 week

Using awk and a column, I display the contents of the report variable on the screen in three columns.

➜ echo $report | awk '{a[NR%7] = a[NR%7] (NR<=7 ? "" : ",") $0} END{for (i = 1; i <= 7; i++) print a[i%7]}' | column -t -s ','

 Hostname : or-05-arm                      Hostname : contabo-3                       Hostname : contabo-2
 CheckDate: 11:47 27-10-2022               CheckDate: 14:47 27-10-2022                CheckDate: 14:47 27-10-2022
 CPU Usage: [..........] 0%  [0.00/2]      CPU Usage: [#####.....] 50% [3.01/6]       CPU Usage: [#####.....] 53% [3.16/6]
 RAM Usage: [###.......] 31% [1.8G/5.8G]   RAM Usage: [###.......] 30% [4.7G/15.6G]   RAM Usage: [##........] 25% [3.9G/15.6G]
 HDD Avail: 26G                            HDD Avail: 136G                            HDD Avail: 176G
 Uptime   : up 19 weeks                    Uptime   : up 1 week                       Uptime   : up 1 week

This works as long as no more than three servers are stored in the variable. If there are more than three servers, then the output does not fit on the screen.

How to make 3 servers appear on the screen, then three more on the next line, and so on?


Solution

  • Assumptions:

    • each block of text contains exactly 6 lines (not counting blank lines)
    • the last line of each block of text includes the string Uptime
    • Uptime does not show up in any other lines

    Setup (7 blocks of data):

    $ cat report.dat
     Hostname : or-05-arm
     CheckDate: 11:47 27-10-2022
     CPU Usage: [..........] 0%  [0.00/2]
     RAM Usage: [###.......] 31% [1.8G/5.8G]
     HDD Avail: 26G
     Uptime   : up 19 weeks
    
     Hostname : contabo-3
     CheckDate: 14:47 27-10-2022
     CPU Usage: [#####.....] 50% [3.01/6]
     RAM Usage: [###.......] 30% [4.7G/15.6G]
     HDD Avail: 136G
     Uptime   : up 1 week
    
     Hostname : contabo-2
     CheckDate: 14:47 27-10-2022
     CPU Usage: [#####.....] 53% [3.16/6]
     RAM Usage: [##........] 25% [3.9G/15.6G]
     HDD Avail: 176G
     Uptime   : up 1 week
    
     Hostname : or-05-arm
     CheckDate: 11:47 27-10-2022
     CPU Usage: [..........] 0%  [0.00/2]
     RAM Usage: [###.......] 31% [1.8G/5.8G]
     HDD Avail: 26G
     Uptime   : up 19 weeks
    
     Hostname : contabo-3
     CheckDate: 14:47 27-10-2022
     CPU Usage: [#####.....] 50% [3.01/6]
     RAM Usage: [###.......] 30% [4.7G/15.6G]
     HDD Avail: 136G
     Uptime   : up 1 week
    
     Hostname : contabo-2
     CheckDate: 14:47 27-10-2022
     CPU Usage: [#####.....] 53% [3.16/6]
     RAM Usage: [##........] 25% [3.9G/15.6G]
     HDD Avail: 176G
     Uptime   : up 1 week
    
     Hostname : or-05-arm
     CheckDate: 11:47 27-10-2022
     CPU Usage: [..........] 0%  [0.00/2]
     RAM Usage: [###.......] 31% [1.8G/5.8G]
     HDD Avail: 26G
     Uptime   : up 19 weeks
    

    Adding a couple counters and a function (to print 3x blocks at a time) to OP's current awk code:

    awk '
    
    function print_block() {
        for (i = 1; i <= 7; i++)
            print a[i%7]
        print ","                           # column will convert this to a blank line
        delete a                            # clear array
        upcount=linecount=0                 # reset counters
    }
    
    ! NF       { next }                     # skip blank lines
               { linecount++ }
               { a[linecount % 7] = a[linecount % 7] (linecount <= 7 ? "" : ",") $0 }
    
    /Uptime/   { upcount++ }
    upcount>=3 { print_block() }            # dump array to stdout for each 3rd occurrence of "Uptime"
    END        { print_block() }            # flush anything still in array once all input has been processed
    
    ' report.dat | column -t -s,
    

    This generates:

     Hostname : or-05-arm                      CheckDate: 14:47 27-10-2022                CPU Usage: [#####.....] 53% [3.16/6]
     CheckDate: 11:47 27-10-2022               CPU Usage: [#####.....] 50% [3.01/6]       RAM Usage: [##........] 25% [3.9G/15.6G]
     CPU Usage: [..........] 0%  [0.00/2]      RAM Usage: [###.......] 30% [4.7G/15.6G]   HDD Avail: 176G
     RAM Usage: [###.......] 31% [1.8G/5.8G]   HDD Avail: 136G                            Uptime   : up 1 week
     HDD Avail: 26G                            Uptime   : up 1 week
     Uptime   : up 19 weeks                    Hostname : contabo-2
     Hostname : contabo-3                      CheckDate: 14:47 27-10-2022
    
     Hostname : or-05-arm                      CheckDate: 14:47 27-10-2022                CPU Usage: [#####.....] 53% [3.16/6]
     CheckDate: 11:47 27-10-2022               CPU Usage: [#####.....] 50% [3.01/6]       RAM Usage: [##........] 25% [3.9G/15.6G]
     CPU Usage: [..........] 0%  [0.00/2]      RAM Usage: [###.......] 30% [4.7G/15.6G]   HDD Avail: 176G
     RAM Usage: [###.......] 31% [1.8G/5.8G]   HDD Avail: 136G                            Uptime   : up 1 week
     HDD Avail: 26G                            Uptime   : up 1 week
     Uptime   : up 19 weeks                    Hostname : contabo-2
     Hostname : contabo-3                      CheckDate: 14:47 27-10-2022
    
     Hostname : or-05-arm
     CheckDate: 11:47 27-10-2022
     CPU Usage: [..........] 0%  [0.00/2]
     RAM Usage: [###.......] 31% [1.8G/5.8G]
     HDD Avail: 26G
     Uptime   : up 19 weeks
    

    If OP has pre-loaded all of the data into a variable then there a couple options:

    echo "${report}" | awk '... see code from above ...'                 | column -t -s,
    
    # or
                       awk '... see code from above ...' <<< "${report}" | column -t -s,
    

    NOTE: ${report} must be wrapped in double quotes