So I have been searching like crazy, but I still have not found a satisfactory solution. I have some output which looks like the following
kdeconnec 1625 1000 11u IPv6 414426 0t0 UDP *:1716
vivaldi-b 1937 1000 263u IPv4 440390 0t0 UDP 224.0.0.251:5353
electron 9522 1000 23u IPv4 414465 0t0 TCP 192.168.0.17:58692->157.240.194.18:443 (ESTABLISHED)
flask 27084 1000 3u IPv4 109532 0t0 TCP 127.0.0.1:3000 (LISTEN)
firefox 27094 1000 99u IPv4 425877 0t0 TCP 192.168.0.17:34114->54.191.222.112:443 (ESTABLISHED)
python 36425 1000 3u IPv4 109532 0t0 TCP 127.0.0.1:3000 (LISTEN)
chromium 110937 1000 130u IPv4 439461 0t0 UDP 224.0.0.251:5353
I want to apply a function called exec_path_from_process_id
to each value in the second column, and insert it as the second column. Resulting in the following. The exact formating (alignment) is not important, as long as it is aligned.
kdeconnec /usr/lib/kdeconnectd 1625 1000 11u IPv6 414426 0t0 UDP *:1716
vivaldi-b /opt/vivaldi/vivaldi-bin 1937 1000 263u IPv4 440390 0t0 UDP 224.0.0.251:5353
electron /usr/lib/electron/electron 9522 1000 23u IPv4 414465 0t0 TCP 192.168.0.17:58692->157.240.194.18:443 (ESTABLISHED)
flask /usr/bin/python3.10 27084 1000 3u IPv4 109532 0t0 TCP 127.0.0.1:3000 (LISTEN)
firefox /usr/lib/firefox/firefox 27094 1000 99u IPv4 425877 0t0 TCP 192.168.0.17:34114->54.191.222.112:443 (ESTABLISHED)
python /usr/bin/python3.10 36425 1000 3u IPv4 109532 0t0 TCP 127.0.0.1:3000 (LISTEN)
chromium /usr/lib/chromium/chromium 110937 1000 130u IPv4 439461 0t0 UDP 224.0.0.251:5353
kioslave5 /usr/lib/kf5/kioslave5 133514 1000 6u IPv4 499063 0t0 TCP 192.168.0.17:54238->84.208.4.225:443 (ESTABLISHED)
My current code is a hot mess, but I got it working at least. The only restraint is that it has to work on bash 3.2+
listeners=$(
lsof -Pnl +M -i |
awk -F" " '!_[$1]++' |
tail -n +2
)
function exec_path_from_process_id () {
local pid="${1}"
path=$(readlink -f /proc/"$pid"/exe)
if [ -z "${path}" ]; then
path=$(awk '{print $(NF)}' <<< $(ls -alF /proc/"$pid"/exe))
fi
echo ${path:-null}
}
pids=($(awk '{ print $2 }' <<< "$listeners"))
IFS=$'\n' read -rd '' -a listeners_array <<< "$listeners"
IFS=$'\n' read -rd '' -a paths <<< $(for i in "${pids[@]}"; do exec_path_from_process_id "$i"; done)
for i in "${!pids[@]}"; do
row="${listeners_array[i]}"
row=$(awk -v r="${paths[i]}" '{ print $1 " " r " " $2 " " $3 " " $4 " " $5 " " $6 " " $7 " " $8 " " $9 " " $10}' <<< $row)
printf '%s\n' "${row[@]}"
done | column -t
realpath -m
instead perhapscolumn -t
to 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)>
.