最初の列に関数を適用し、2番目の列に挿入

最初の列に関数を適用し、2番目の列に挿入

だからクレイジーなように探してみましたが、まだ満足のいく解決策が見つかりませんでした。以下のように出力があります。

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 

exec_path_from_process_id各値に次の関数を適用したいと思います。第二列を次のように挿入します。第二柱。結果は次のとおりです。正確な形式(ソート)は重要ではなく、ソートするだけです。

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)

私の現在のコードはめちゃくちゃですが、少なくともうまくいきました。唯一の制限は、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

答え1

たぶん、次のようなものがあります。

lsof -Pnl +M -i | awk '
# Use: NR > 1 to skip header
NR > 1 && !x[$1]++ {
    # realpath -m
    # (no path components need exist or be a directory)
    cmd = "realpath -m /proc/"$2"/exe"
    cmd | getline path
    close(cmd)
    # We can edit field $2 and print $0
    $2 = path" "$2
    print $0
}' | column -t

この行はcmd | getline pathコマンドを実行しcmd、出力を変数として読み込みますpath。誰かが閉じないとコマンドは閉じられないので、close(expression)変数に入れました。

答え2

フィールドが整列している場合は、書式設定を気にせず、必要に応じて十分に大きい幅を選択してから、次のことを行ったと述べました。

$ 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

上記は、最初の列にスペースがないと仮定しています。

明らかに<$(wc -c <<<"$pid")>実行する必要がある実際のコマンドに変更し、最初のコマンドを%-10sそのコマンドが出力できる最大幅の文字列に変更するだけです。この幅に使用できる最大値がないと思われる場合は、お知らせください。2段階のアプローチが使用されます。つまり、ステップ1は出力を生成し、ステップ2は出力フォーマットを指定します。使用している形式が満足できる場合は、column -t次のようになります(明らかに実際に使用できないfile形式に置き換えます)。<(lsof -Pnl +M -i)

$ 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

ただし、pidで実行されるコマンドの出力など、行の一部にスペースが含まれていると失敗します。

あなたが要求したように、以下は2段階の方法です。

  1. 上記のように、スペースで区切られたフィールドと改行で区切られたレコードを使用してテキストを出力するのではなく、改行で区切られたフィールドとNULで区切られたレコードを使用して出力を生成します。
while read -r a pid b; do printf "%s\n%s\n%s\n%s\0" "$a" "<$(wc -c <<<"$pid")>" "$pid" "$b"; done < file
  1. 改行で区切られたフィールドを含むNUL区切りレコードを読み取り、入力を読み取るときに各フィールドの最大幅を計算し、出力を印刷するときに各フィールドをその幅に出力し、フィールドを単一のOKに再グループ化するawkスクリプトを作成します。
$ 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

これには、GNU awkなど、NULで区切られた入力を読み取ることができるawkが必要です。パス名または他のフィールドに改行文字を含めることはできないとします。

上記の操作をすべて1つのawkスクリプトで実際に実行するには、外部コマンドを呼び出すたびにawkがサブシェルを分離する必要があります。http://awk.freeshell.org/AllAboutGetline) しかし、ここでは、入力フィールドの空白を維持することに興味はありませんが、パスの改行ではない空白は大丈夫だとします。

$ 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

関連情報