You said you don't care about formatting as long as the fields are aligned so, just pick a width that'll be wide enough for your needs and then:
$ while read -r a pid b; do printf "%-12s%-10s%10s %s\n" "$a" "<$(wc -c <<<"$pid")>" "$pid" "$b" done < <(lsof -Pnl +M -i) kdeconnec <5> 1625 1000 11u IPv6 414426 0t0 UDP *:1716 vivaldi-b <5> 1937 1000 263u IPv4 440390 0t0 UDP 224.0.0.251:5353 electron <5> 9522 1000 23u IPv4 414465 0t0 TCP 192.168.0.17:58692->157.240.194.18:443 (ESTABLISHED) flask <6> 27084 1000 3u IPv4 109532 0t0 TCP 127.0.0.1:3000 (LISTEN) firefox <6> 27094 1000 99u IPv4 425877 0t0 TCP 192.168.0.17:34114->54.191.222.112:443 (ESTABLISHED) python <6> 36425 1000 3u IPv4 109532 0t0 TCP 127.0.0.1:3000 (LISTEN) chromium <7> 110937 1000 130u IPv4 439461 0t0 UDP 224.0.0.251:5353
The above assumes your first column doesn't contain any spaces.
Obviously just change <$(wc -c <<<"$pid")> to whatever the real command is you need to run and the first %-10s to be whatever max width string that command could output. If you REALLY feel there is no max value you could use for that width, let us know as then it'd take a 2-pass approach - 1 to produce the output and then 2 to format the output. If you're happy with using column -t for the formatting then it'd be (replace file with <(lsof -Pnl +M -i) which obviously I don't really have available):
$ while read -r a pid b; do printf "%s %s %s %s\n" "$a" "<$(wc -c <<<"$pid")>" "$pid" "$b" done < file | column -t kdeconnec <5> 1625 1000 11u IPv6 414426 0t0 UDP *:1716 vivaldi-b <5> 1937 1000 263u IPv4 440390 0t0 UDP 224.0.0.251:5353 electron <5> 9522 1000 23u IPv4 414465 0t0 TCP 192.168.0.17:58692->157.240.194.18:443 (ESTABLISHED) flask <6> 27084 1000 3u IPv4 109532 0t0 TCP 127.0.0.1:3000 (LISTEN) firefox <6> 27094 1000 99u IPv4 425877 0t0 TCP 192.168.0.17:34114->54.191.222.112:443 (ESTABLISHED) python <6> 36425 1000 3u IPv4 109532 0t0 TCP 127.0.0.1:3000 (LISTEN) chromium <7> 110937 1000 130u IPv4 439461 0t0 UDP 224.0.0.251:5353
but that would fail if any part of your line contained spaces, e.g. the output of the command you're running on the pid.
Since you asked, here's a 2-pass approach:
- Instead of outputting text that has spaces separating fields and newlines separating records as above, produce output that uses newlines to separate fields and NUL to separate records:
while read -r a pid b; do printf "%s\n%s\n%s\n%s\0" "$a" "<$(wc -c <<<"$pid")>" "$pid" "$b"; done < file
- Write an awk script that reads NUL-separated records containing newline-separated fields, calculate the max width of each field when reading the input and output each field in that width when printing the output, recombining the fields into single lines:
$ while read -r a pid b; do printf "%s\n%s\n%s\n%s\0" "$a" "<$(wc -c <<<"$pid")>" "$pid" "$b"; done < file | awk -v RS='\0' -F'\n' ' { recs[NR]=$0; for (i=1; i<=NF; i++) wids[i]=(length($i)>wids[i] ? length($i) : wids[i]) } END { for (n=1; n<=NR; n++) { $0=recs[n]; for (i=1;i<=NF;i++) printf "%-*s%s", wids[i], $i, (i<NF ? OFS : ORS) } } ' kdeconnec <5> 1625 1000 11u IPv6 414426 0t0 UDP *:1716 vivaldi-b <5> 1937 1000 263u IPv4 440390 0t0 UDP 224.0.0.251:5353 electron <5> 9522 1000 23u IPv4 414465 0t0 TCP 192.168.0.17:58692->157.240.194.18:443 (ESTABLISHED) flask <6> 27084 1000 3u IPv4 109532 0t0 TCP 127.0.0.1:3000 (LISTEN) firefox <6> 27094 1000 99u IPv4 425877 0t0 TCP 192.168.0.17:34114->54.191.222.112:443 (ESTABLISHED) python <6> 36425 1000 3u IPv4 109532 0t0 TCP 127.0.0.1:3000 (LISTEN) chromium <7> 110937 1000 130u IPv4 439461 0t0 UDP 224.0.0.251:5353
That requires an awk that can read NUL-separated input, e.g. GNU awk. It assumes that none of your path names or other fields can contain newlines.
If you REALLY wanted to do all of the above in a single awk script, that means awk would have to spin off a subshell every time your external command is called which would be slow and you'd have to ensure you get the quoting right (see http://awk.freeshell.org/AllAboutGetline) but here you go, assuming no spaces that you care about retaining within fields in your input but non-newline spaces in paths would be fine:
$ awk ' { recs[NR] = $0 for (i=1; i<=NF; i++) { lgth = length($i) wids[i] = ( lgth > wids[i] ? lgth : wids[i] ) } cmd = "wc -c <<<\047" $2 "\047" paths[NR] = ( (cmd | getline line) > 0 ? line : "N/A" ) close(cmd) lgth = length(paths[NR]) pathWid = ( lgth > pathWid ? lgth : pathWid ) } END { for (n=1; n<=NR; n++) { $0 = recs[n] for (i=1; i<=NF; i++) { if ( i == 2 ) { printf "%-*s%s", pathWid, paths[n], OFS } printf "%-*s%s", wids[i], $i, (i<NF ? OFS : ORS) } } } ' < file kdeconnec 5 1625 1000 11u IPv6 414426 0t0 UDP *:1716 vivaldi-b 5 1937 1000 263u IPv4 440390 0t0 UDP 224.0.0.251:5353 electron 5 9522 1000 23u IPv4 414465 0t0 TCP 192.168.0.17:58692->157.240.194.18:443 (ESTABLISHED) flask 6 27084 1000 3u IPv4 109532 0t0 TCP 127.0.0.1:3000 (LISTEN) firefox 6 27094 1000 99u IPv4 425877 0t0 TCP 192.168.0.17:34114->54.191.222.112:443 (ESTABLISHED) python 6 36425 1000 3u IPv4 109532 0t0 TCP 127.0.0.1:3000 (LISTEN) chromium 7 110937 1000 130u IPv4 439461 0t0 UDP 224.0.0.251:5353
realpath -minstead perhapscolumn -tto get alignment since that will split the input at any field that contains blanks, e.g. a file path. You can see in your question it's already splitting the final field on each line into 2 separate fields, e.g.<127.0.0.1:3000 (LISTEN)>becomes<127.0.0.1:3000> <(LISTEN)>.