2

The goal

We're looking to change this output (generated from iptables(8)-based script below running on Ubuntu 18.04 that parses the original, raw, anonymized iptables -nvL output from one of our servers):

Chain INPUT (policy DROP 2525 packets, 130K bytes)
target                      prot        opt      in           out   source          destination
ufw-before-logging-input    all         --       *            *     0.0.0.0/0       0.0.0.0/0
ufw-before-input            all         --       *            *     0.0.0.0/0       0.0.0.0/0
ufw-after-input             all         --       *            *     0.0.0.0/0       0.0.0.0/0
ufw-after-logging-input     all         --       *            *     0.0.0.0/0       0.0.0.0/0
ufw-reject-input            all         --       *            *     0.0.0.0/0       0.0.0.0/0
ufw-track-input             all         --       *            *     0.0.0.0/0       0.0.0.0/0

...to be narrower, with less unnecessary whitespace between the columns:

Chain INPUT (policy DROP 2525 packets, 130K bytes)
target                      prot   opt   in   out   source          destination
ufw-before-logging-input    all    --    *    *     0.0.0.0/0       0.0.0.0/0
ufw-before-input            all    --    *    *     0.0.0.0/0       0.0.0.0/0
ufw-after-input             all    --    *    *     0.0.0.0/0       0.0.0.0/0
ufw-after-logging-input     all    --    *    *     0.0.0.0/0       0.0.0.0/0
ufw-reject-input            all    --    *    *     0.0.0.0/0       0.0.0.0/0
ufw-track-input             all    --    *    *     0.0.0.0/0       0.0.0.0/0

We'd prefer a bash script where we can more-easily (than in the script below) tweak, over time, the width of each column spacing on a per-column basis. We've not yet been able to get awk-based things like sprintf, gsub, or this mechanism to do what we want.

Non-bash solutions that accomplish similar/same things might also work.

More-challenging input

This section of the iptables -nvL output (unparsed by the script below) is more difficult, because of the right-most free-form column, the only column with spaces in the content:

Chain ufw-after-input (1 references)
 pkts bytes target     prot opt in     out     source               destination         
    1    78 ufw-skip-to-policy-input  udp  --  *      *       0.0.0.0/0            0.0.0.0/0            udp dpt:137
    0     0 ufw-skip-to-policy-input  udp  --  *      *       0.0.0.0/0            0.0.0.0/0            udp dpt:138
    0     0 ufw-skip-to-policy-input  tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            tcp dpt:139
    5   260 ufw-skip-to-policy-input  tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            tcp dpt:445
    0     0 ufw-skip-to-policy-input  udp  --  *      *       0.0.0.0/0            0.0.0.0/0            udp dpt:67
    0     0 ufw-skip-to-policy-input  udp  --  *      *       0.0.0.0/0            0.0.0.0/0            udp dpt:68
    0     0 ufw-skip-to-policy-input  all  --  *      *       xxx.xxx.xxx.xxx/yy   xxx.xxx.xxx.xxx/yy   ADDRTYPE match dst-type BROADCAST

Chain ufw-after-logging-forward (1 references)
 pkts bytes target     prot opt in     out     source               destination         
    0     0 LOG        all  --  *      *       xxx.xxx.xxx.xxx/yy   xxx.xxx.xxx.xxx/yy   limit: avg 3/min burst 10 LOG flags 0 level 4 prefix "[UFW BLOCK] "

A good conversion of the above looks like this:

Chain ufw-after-input (1 references)
target                    prot opt in  out  source              destination
ufw-skip-to-policy-input  udp  --  *   *    0.0.0.0/0           0.0.0.0/0           udp dpt:137
ufw-skip-to-policy-input  udp  --  *   *    0.0.0.0/0           0.0.0.0/0           udp dpt:138
ufw-skip-to-policy-input  tcp  --  *   *    0.0.0.0/0           0.0.0.0/0           tcp dpt:139
ufw-skip-to-policy-input  tcp  --  *   *    0.0.0.0/0           0.0.0.0/0           tcp dpt:445
ufw-skip-to-policy-input  udp  --  *   *    0.0.0.0/0           0.0.0.0/0           udp dpt:67
ufw-skip-to-policy-input  udp  --  *   *    0.0.0.0/0           0.0.0.0/0           udp dpt:68
ufw-skip-to-policy-input  all  --  *   *    xxx.xxx.xxx.xxx/yy  xxx.xxx.xxx.xxx/yy  ADDRTYPE match dst-type BROADCAST

Chain ufw-after-logging-forward (1 references)
target                    prot opt in  out  source              destination         
LOG                       all  --  *   *    xxx.xxx.xxx.xxx/yy  xxx.xxx.xxx.xxx/yy  limit: avg 3/min burst 10 LOG flags 0 level 4 prefix "[UFW BLOCK] "

Original iptables bash script

The script that generates the first version of the output above; it's arguably a non-elegant hack:

#!/usr/bin/env bash
#
# Pretty-print 'iptables -nvL' output.
# 1. remove the 'pkts' and 'bytes' columns
# 2. make column-aligned, table-based output per
#    https://gist.github.com/Airdawg5571/1a8c49ca5dd97af55ab9
# 3. attempt to make narrower, removing unnecessary whitespace
#    between columns, to save previous display-screen realestate
#
iptables -nvL                                                      | \
    # remove the 'pkts' and 'bytes' columns
    awk '{ if ($1 != "Chain") { $1=""; $2=""; print } else print}' | \
    # make tabular (table) output
    column -t                                                      | \
    sed 's/^Chain/\n&/g'                                           | \
    sed '/^Chain/ s/[ \t]\{1,\}/ /g'                               | \
    sed '/^[0-9]/ s/[ \t]\{1,\}/ /10g'                             | \
    # reduce the unnecessary whitespace after 'prot' column
    perl -pe 's|^(?!Chain)([^\s]+\s+\w+)\s+|\1  \t|'               | \
    # arbitrarily truncate line length
    cut -c -120
Johnny Utahh
  • 2,389
  • 3
  • 25
  • 41
  • Is this not a remove line1 and use `column -t` problem? That's `head -n1 file && sed 1d file | column -t`. But you have `column -t` in your script so I'm not sure what the problem is. – stevesliva May 17 '20 at 04:07
  • [edit] your question to provide sample input (i.e. the output of `iptables -nvL` **before its piped to all those other commands** as we'll get rid of all of those except maybe `column`) and the expected output given that input. – Ed Morton May 17 '20 at 13:05
  • Redundant `print`: `awk '{ if ($1 != "Chain") { $1=""; $2=""; print } else print}'` can be simplified to `awk '{ if ($1 != "Chain") { $1=""; $2="" } print}'` – agc May 17 '20 at 14:35
  • @EdMorton - original, raw `iptables -nvL` output added above. Here's the link: https://gist.githubusercontent.com/johnnyutahh/fdccf18b2c6529e17252cfca9703185e/raw – Johnny Utahh May 17 '20 at 16:47
  • Please don't post links (or images), simply show a [mcve] that includes concise, testable sample input and the expected output given that input as text in your question. See [ask] if that's not clear. I just took a glance at the text in that link you posted which prompted this - when posting your sample input/output, make sure it's **minimal** but still representative of your real problem. So if your real data is 20 blocks of 10-20 lines each, create and post a representative example using 2 or 3 blocks of 4 or 5 lines each. Keep it clear, brief, and simple for us. – Ed Morton May 17 '20 at 16:53
  • @agc wrt [your comment](https://stackoverflow.com/questions/61845494/selectively-remove-unnecessary-column-whitespace-from-iptables-nvl-command-ou/61852526#comment109401437_61845494) - `awk '{ if ($1 != "Chain") { $1=""; $2="" } print}'` can be written as just `awk '$1 != "Chain"{$1=$2=""} 1'` – Ed Morton May 17 '20 at 17:00
  • @EdMorton - I updated question with the more-challenging `iptables -nvL` output section. Does this provide what you seek? – Johnny Utahh May 17 '20 at 17:08
  • Yes. Now - do you **really** want to have to specify widths for the columns on the command line or do you just want a script that puts, say, 2 blank chars between each column? – Ed Morton May 17 '20 at 17:09
  • @EdMorton - some sort of "easy programmable" width is preferred. Does not need to be on the cmdline, I'm happy to edit the script. (eg: the `tabs=(0 0 12 5 4 8 8 17 17 12 17)` portion of [this script](https://www.reddit.com/r/bash/comments/gl61yb/selectively_remove_unnecessary_column_whitespace/fqw19tv/).) Regardless, all columns in the entire output must align. This enables my team to more-easily tweak the conversion based upon potentially-highly variable `iptables` output on a per-system basis. (Many thanks for all your help with this!) Does this answer your question? – Johnny Utahh May 17 '20 at 17:12
  • OK, I updated [my answer](https://stackoverflow.com/a/61852526/1745001) but I really think you'd be better off just letting awk figure out the minimim field widths required to display the data rather than hard-coding them, expeciall given you said the input will be `highly variable`. If you don't want to do that then you should figure out what you want to do with fields where the input data is longer than the width you've allowed for it - truncate it, let it expand and throw the tabular look off, or wrap it within it's column. – Ed Morton May 17 '20 at 17:29

3 Answers3

2

Given your updated question and comments (and using cat file in place of iptables -nvL which I don't have on my system)::

$ cat tst.awk
BEGIN { nf = split("0 0 27 7 6 5 5 20 20",w) }
NF && !/^Chain/ {
    for (i=3; i<=nf; i++) {
        printf "%-*s", w[i], $i
    }
    sub("^([[:space:]]*[^[:space:]]+){"nf"}[[:space:]]*","")
}
{ print }

.

$ cat file | awk -f tst.awk
Chain ufw-after-input (1 references)
target                     prot   opt   in   out  source              destination
ufw-skip-to-policy-input   udp    --    *    *    0.0.0.0/0           0.0.0.0/0           udp dpt:137
ufw-skip-to-policy-input   udp    --    *    *    0.0.0.0/0           0.0.0.0/0           udp dpt:138
ufw-skip-to-policy-input   tcp    --    *    *    0.0.0.0/0           0.0.0.0/0           tcp dpt:139
ufw-skip-to-policy-input   tcp    --    *    *    0.0.0.0/0           0.0.0.0/0           tcp dpt:445
ufw-skip-to-policy-input   udp    --    *    *    0.0.0.0/0           0.0.0.0/0           udp dpt:67
ufw-skip-to-policy-input   udp    --    *    *    0.0.0.0/0           0.0.0.0/0           udp dpt:68
ufw-skip-to-policy-input   all    --    *    *    xxx.xxx.xxx.xxx/yy  xxx.xxx.xxx.xxx/yy  ADDRTYPE match dst-type BROADCAST

Chain ufw-after-logging-forward (1 references)
target                     prot   opt   in   out  source              destination
LOG                        all    --    *    *    xxx.xxx.xxx.xxx/yy  xxx.xxx.xxx.xxx/yy  limit: avg 3/min burst 10 LOG flags 0 level 4 prefix "[UFW BLOCK] "

When run against the sample input provided at https://gist.githubusercontent.com/johnnyutahh/fdccf18b2c6529e17252cfca9703185e/raw I get the following, which I don't see any issues in so if as you say this output isn't correct, you'd have to point out where the problem is:

$ awk -f tst.awk file
Chain INPUT (policy DROP 35 packets, 1771 bytes)
target                     prot   opt   in   out  source              destination
ufw-before-logging-input   all    --    *    *    0.0.0.0/0           0.0.0.0/0
ufw-before-input           all    --    *    *    0.0.0.0/0           0.0.0.0/0
ufw-after-input            all    --    *    *    0.0.0.0/0           0.0.0.0/0
ufw-after-logging-input    all    --    *    *    0.0.0.0/0           0.0.0.0/0
ufw-reject-input           all    --    *    *    0.0.0.0/0           0.0.0.0/0
ufw-track-input            all    --    *    *    0.0.0.0/0           0.0.0.0/0

Chain FORWARD (policy DROP 0 packets, 0 bytes)
target                     prot   opt   in   out  source              destination
ufw-before-logging-forward all    --    *    *    0.0.0.0/0           0.0.0.0/0
ufw-before-forward         all    --    *    *    0.0.0.0/0           0.0.0.0/0
ufw-after-forward          all    --    *    *    0.0.0.0/0           0.0.0.0/0
ufw-after-logging-forward  all    --    *    *    0.0.0.0/0           0.0.0.0/0
ufw-reject-forward         all    --    *    *    0.0.0.0/0           0.0.0.0/0
ufw-track-forward          all    --    *    *    0.0.0.0/0           0.0.0.0/0

Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes)
target                     prot   opt   in   out  source              destination
ufw-before-logging-output  all    --    *    *    0.0.0.0/0           0.0.0.0/0
ufw-before-output          all    --    *    *    0.0.0.0/0           0.0.0.0/0
ufw-after-output           all    --    *    *    0.0.0.0/0           0.0.0.0/0
ufw-after-logging-output   all    --    *    *    0.0.0.0/0           0.0.0.0/0
ufw-reject-output          all    --    *    *    0.0.0.0/0           0.0.0.0/0
ufw-track-output           all    --    *    *    0.0.0.0/0           0.0.0.0/0

Chain ufw-after-forward (1 references)
target                     prot   opt   in   out  source              destination

Chain ufw-after-input (1 references)
target                     prot   opt   in   out  source              destination
ufw-skip-to-policy-input   udp    --    *    *    0.0.0.0/0           0.0.0.0/0           udp dpt:137
ufw-skip-to-policy-input   udp    --    *    *    0.0.0.0/0           0.0.0.0/0           udp dpt:138
ufw-skip-to-policy-input   tcp    --    *    *    0.0.0.0/0           0.0.0.0/0           tcp dpt:139
ufw-skip-to-policy-input   tcp    --    *    *    0.0.0.0/0           0.0.0.0/0           tcp dpt:445
ufw-skip-to-policy-input   udp    --    *    *    0.0.0.0/0           0.0.0.0/0           udp dpt:67
ufw-skip-to-policy-input   udp    --    *    *    0.0.0.0/0           0.0.0.0/0           udp dpt:68
ufw-skip-to-policy-input   all    --    *    *    0.0.0.0/0           0.0.0.0/0           ADDRTYPE match dst-type BROADCAST

Chain ufw-after-logging-forward (1 references)
target                     prot   opt   in   out  source              destination
LOG                        all    --    *    *    0.0.0.0/0           0.0.0.0/0           limit: avg 3/min burst 10 LOG flags 0 level 4 prefix "[UFW BLOCK] "

Chain ufw-after-logging-input (1 references)
target                     prot   opt   in   out  source              destination
LOG                        all    --    *    *    0.0.0.0/0           0.0.0.0/0           limit: avg 3/min burst 10 LOG flags 0 level 4 prefix "[UFW BLOCK] "

Chain ufw-after-logging-output (1 references)
target                     prot   opt   in   out  source              destination

Chain ufw-after-output (1 references)
target                     prot   opt   in   out  source              destination

Chain ufw-before-forward (1 references)
target                     prot   opt   in   out  source              destination
ACCEPT                     all    --    *    *    0.0.0.0/0           0.0.0.0/0           ctstate RELATED,ESTABLISHED
ACCEPT                     all    --    wg0  *    0.0.0.0/0           0.0.0.0/0
ACCEPT                     icmp   --    *    *    0.0.0.0/0           0.0.0.0/0           icmptype 3
ACCEPT                     icmp   --    *    *    0.0.0.0/0           0.0.0.0/0           icmptype 11
ACCEPT                     icmp   --    *    *    0.0.0.0/0           0.0.0.0/0           icmptype 12
ACCEPT                     icmp   --    *    *    0.0.0.0/0           0.0.0.0/0           icmptype 8
ufw-user-forward           all    --    *    *    0.0.0.0/0           0.0.0.0/0

Chain ufw-before-input (1 references)
target                     prot   opt   in   out  source              destination
ACCEPT                     all    --    lo   *    0.0.0.0/0           0.0.0.0/0
ACCEPT                     all    --    *    *    0.0.0.0/0           0.0.0.0/0           ctstate RELATED,ESTABLISHED
ufw-logging-deny           all    --    *    *    0.0.0.0/0           0.0.0.0/0           ctstate INVALID
DROP                       all    --    *    *    0.0.0.0/0           0.0.0.0/0           ctstate INVALID
ACCEPT                     icmp   --    *    *    xxx.xxx.xxx.xxx/yy  0.0.0.0/0           icmptype 8
ACCEPT                     icmp   --    *    *    xxx.xxx.xxx.xxx/yy  0.0.0.0/0           icmptype 8
ACCEPT                     icmp   --    *    *    xxx.xxx.xxx.xxx/yy  0.0.0.0/0           icmptype 8
DROP                       icmp   --    *    *    0.0.0.0/0           0.0.0.0/0           icmptype 3
DROP                       icmp   --    *    *    0.0.0.0/0           0.0.0.0/0           icmptype 11
DROP                       icmp   --    *    *    0.0.0.0/0           0.0.0.0/0           icmptype 12
DROP                       icmp   --    *    *    0.0.0.0/0           0.0.0.0/0           icmptype 8
DROP                       udp    --    *    *    0.0.0.0/0           0.0.0.0/0           udp spt:67 dpt:68
ufw-not-local              all    --    *    *    0.0.0.0/0           0.0.0.0/0
DROP                       udp    --    *    *    0.0.0.0/0           224.0.0.251         udp dpt:5353
DROP                       udp    --    *    *    0.0.0.0/0           239.255.255.250     udp dpt:1900
ufw-user-input             all    --    *    *    0.0.0.0/0           0.0.0.0/0

Chain ufw-before-logging-forward (1 references)
target                     prot   opt   in   out  source              destination

Chain ufw-before-logging-input (1 references)
target                     prot   opt   in   out  source              destination

Chain ufw-before-logging-output (1 references)
target                     prot   opt   in   out  source              destination

Chain ufw-before-output (1 references)
target                     prot   opt   in   out  source              destination
ACCEPT                     all    --    *    lo   0.0.0.0/0           0.0.0.0/0
ACCEPT                     all    --    *    *    0.0.0.0/0           0.0.0.0/0           ctstate RELATED,ESTABLISHED
Ed Morton
  • 188,023
  • 17
  • 78
  • 185
  • 1
    Hi Ed - neither `tst.awk` script above completely works. [This does](https://stackoverflow.com/a/61856775/605356). I need it to work for the full the [`iptables -nvL` output](https://gist.githubusercontent.com/johnnyutahh/fdccf18b2c6529e17252cfca9703185e/raw). Thanks for all your help and effort, very appreciated, and your answer and notes tremendously helps me learn awk - something I've been seeking for some time. – Johnny Utahh May 17 '20 at 18:44
  • If you tell me in what way one of them (whichever is closest to your needs) doesn't completely work then I can help you correct any issues. If there's something in the output you linked that's not present in the example you posted in your question and the script doesn't handle that missing thing correctly then fix the example in your question. Don't use that shell script you posted, see [why-is-using-a-shell-loop-to-process-text-considered-bad-practice](https://unix.stackexchange.com/questions/169716/why-is-using-a-shell-loop-to-process-text-considered-bad-practice). – Ed Morton May 17 '20 at 18:48
  • Hi Ed, I've provided the [full `iptables -nvL` output](https://gist.githubusercontent.com/johnnyutahh/fdccf18b2c6529e17252cfca9703185e/raw). If you seek more info, you are welcome to run against this data and clearly see the problems--they are very obvious. And shell loops may be non-optimal, but it's solving my problem without any security problems that I can see. Signing off from this thread - thanks again (hugely) Ed for all your help! – Johnny Utahh May 17 '20 at 18:51
  • @JohnnyUtahh against my better judgement I did run that last script I posted against the sample input in that link you provided and posted the output towards the top of my answer under the script. I don't see any issues with it. If you see any issues please do let me know where. – Ed Morton May 17 '20 at 19:21
  • The above scripts mangle [the full iptables output](https://gist.githubusercontent.com/johnnyutahh/fdccf18b2c6529e17252cfca9703185e/raw), paragraph headers are gone, no right-most column, maybe other problems; [here's the output from the 2nd script](https://gist.githubusercontent.com/johnnyutahh/d32e5f2f22180c4b02c1d654592bdfa0/raw), which very-obviously shows the problems when comparing the data from the 2 links in this post. Later, ~J – Johnny Utahh May 17 '20 at 19:29
  • Don't say "scripts", there is 1 single script suggested in my answer and it's the one at the top of it, the other scripts listed later under "Original answer:" are prior versions which I created before you had posted usable sample input and which we've already discarded for this but I thought might be useful for others reading this in future with similar problems so I left them in place. The output you posted at https://gist.githubusercontent.com/johnnyutahh/d32e5f2f22180c4b02c1d654592bdfa0/raw is NOT the output that the script in my answer produces - I included that output in my answer. – Ed Morton May 17 '20 at 19:33
  • 1
    I just removed everything from "Original answer" down to avoid any further confusion of which script I'm suggesting would solve your problem. – Ed Morton May 17 '20 at 19:38
  • Ed, There are multiple scripts above, and the 1st one works, and appears to manage widths dynamically - that's fantastic - thank you very much! (I'd delete any obsolete content if you were you, to avoid future confusion, but you're welcome to do whatever you want.) – Johnny Utahh May 17 '20 at 19:40
  • Correction: it's not dynamic. But it still works. Thanks! – Johnny Utahh May 17 '20 at 19:41
  • You're welcome. When I asked previously [you said](https://stackoverflow.com/questions/61845494/selectively-remove-unnecessary-column-whitespace-from-iptables-nvl-command-ou/61852526?noredirect=1#comment109405469_61845494) you did not want it to be dynamic otherwise I would have written it to figure out the widths dynamically as I had in one of the other scripts that I just deleted from my answer. – Ed Morton May 17 '20 at 19:43
  • A working dynamic solution would be fantastic. I'm concerned it will take some effort to work in all cases (and even more effort to test it) and may not be worth anyone's time. But you're welcome to give it a shot if it pleases you to do so. – Johnny Utahh May 17 '20 at 19:46
  • No, I already showed you how to do that in one of the now-deleted scripts so you can trivially tweak the current script to do that if you like by referring to that original answer (assuming you saved it somewhere - I didn't). – Ed Morton May 17 '20 at 19:48
  • The one I thought was dynamic was one of the awk-based scripts that did not work. Alas, sorry Ed, I'm unable to keep up with all the edits. Warm regards, ~J – Johnny Utahh May 17 '20 at 19:50
2

This works:

#!/usr/bin/env bash

#
# Pretty-print iptables(8) output.
#
# source:
# https://gitlab.com/johnnyutahh/swmisc/-/blob/master/sysadmin/networking/iptables/iptables-list-pretty.sh
#
# (The following script was Ubuntu-18.04 tested on 2020-05-17.)
#

# Derivered from
# https://www.reddit.com/r/bash/comments/gl61yb/selectively_remove_unnecessary_column_whitespace/fqw19tv

# Adjust these values to resize column widths
column_widths=(0 0 27 5 4 8 8 17 17)

iptables_align()
{
  while read line; do
    if [[ $line =~ Chain ]]; then
      echo "$line"
    else
      line=${line//\*/\\\*}
      array=($line)
      for n in {2..8}; do
        w=${column_widths[$n]}
         printf "%-${w}s" "${array[$n]}"
      done
      lastcol_with_spaces_in_content=("${array[@]:9}")
      printf "%s" "${lastcol_with_spaces_in_content[*]}"
      echo
    fi
  done
}

iptables -nvL | iptables_align | sed -s 's|\\\*|* |g' | less

Source:

https://gitlab.com/johnnyutahh/swmisc/-/blob/master/sysadmin/networking/iptables/iptables-list-pretty.sh

Johnny Utahh
  • 2,389
  • 3
  • 25
  • 41
  • I know you're happy with it so this is more for others with a similar issue coming upon this answer: You should run that through shellcheck (e.g. https://www.shellcheck.net/) as a starting point to finding and fixing some of the issues. Even once you fix those and find/fix any less obvious other issues that shellcheck doesn't identify it'll still be immensely slow, non-portable, and long. See [why-is-using-a-shell-loop-to-process-text-considered-bad-practice](https://unix.stackexchange.com/questions/169716/why-is-using-a-shell-loop-to-process-text-considered-bad-practice) for more information. – Ed Morton May 17 '20 at 19:10
  • Nice! Some might want the following changes: `column_widths=(10 10 10 28 6 4 24 24 18 18)`, `for n in {0..9}; do`, and `iptables --line-numbers -nvL | iptables_align | sed -s 's|\\\*|* |g' | less;`. Might help with Docker containers running, for example. – Artfaith Aug 09 '22 at 17:39
1

Using Steve Kinzler's align util:

align -e '!/^Chain/' -g 4 < file.txt

Output, (the -e prevents the first line from being aligned, and the -g sets the minimum gutter to 4 spaces):

Chain INPUT (policy DROP 2525 packets, 130K bytes)
target                      prot    opt    in    out    source       destination
ufw-before-logging-input    all     --     *     *      0.0.0.0/0    0.0.0.0/0
ufw-before-input            all     --     *     *      0.0.0.0/0    0.0.0.0/0
ufw-after-input             all     --     *     *      0.0.0.0/0    0.0.0.0/0
ufw-after-logging-input     all     --     *     *      0.0.0.0/0    0.0.0.0/0
ufw-reject-input            all     --     *     *      0.0.0.0/0    0.0.0.0/0
ufw-track-input             all     --     *     *      0.0.0.0/0    0.0.0.0/0

Per-column formatting: the -a switch controls the format style, but can't set individual column widths. Example -- right align the first column, use the default alignment for the next four, then right align the rest:

align -e '!/^Chain/' -a r4dr < file.txt 

Output:

Chain INPUT (policy DROP 2525 packets, 130K bytes)
                  target prot opt in out    source destination
ufw-before-logging-input all  --  *  *   0.0.0.0/0   0.0.0.0/0
        ufw-before-input all  --  *  *   0.0.0.0/0   0.0.0.0/0
         ufw-after-input all  --  *  *   0.0.0.0/0   0.0.0.0/0
 ufw-after-logging-input all  --  *  *   0.0.0.0/0   0.0.0.0/0
        ufw-reject-input all  --  *  *   0.0.0.0/0   0.0.0.0/0
         ufw-track-input all  --  *  *   0.0.0.0/0   0.0.0.0/0
Johnny Utahh
  • 2,389
  • 3
  • 25
  • 41
agc
  • 7,973
  • 2
  • 29
  • 50
  • That first script is equivalent to `awk 'NR==1{print; next} {print | "column -t -o\" \""}'` and the second `awk 'NR==1{print; next} {print | "column -t -R\"1,6,7\""}'` using GNU `column` so there's no need to install a private tool for that plus you can easily add the blank line before the `Chain` line that the OP wants inside the awk script using `awk 'NR==1{print ORS $0; next} ...`. – Ed Morton May 17 '20 at 15:06
  • [*Steve Kinzler's* **`align`** util](https://kinzler.com/me/align/) is a fantastic find. Not sure it can handle the "last, rightmost, free-form" column this can. But I can certainly use it in other arenas. Thanks @agc! – Johnny Utahh May 17 '20 at 18:47
  • @EdMorton, On my system (Mint *v19.3*, `mawk` *v1.3.3-17ubuntu3*) the first two `awk` equivalents fail with `column: invalid option ...` errors. – agc May 18 '20 at 07:00
  • @argc I said "using GNU column" and evidently you aren't using GNU column. The awk version is irrelevant, it'll behave the same with any awk. My point is that if you can get similar functionality then it's better for portability, reliability, etc. to install/use GNU tools that are used by thousands (millions?) of people on as many boxes and so is well-tested/exercised and supported by an established organization than someones private tool that may only be used by a handful of people, may contain bugs those people haven't tripped over yet, and availability/support of which might just go away. – Ed Morton May 18 '20 at 15:26
  • 1
    @EdMorton, Good call on *GNU* `column`, I'm surprised to learn that *Mint* uses *bsdmainutils* to provide `column`. – agc May 19 '20 at 04:49