Issue
I use the following command to output PID and FD related to CLOSE-WAIT sockets.
sudo ss -p | grep CLOSE-WAIT | awk '{ print $7 }' | sed 's/,pid=/ /' | sed 's/,fd=/ /' | sed 's/))//' | awk '{ print $2, $3 }'
It outputs correctly where a PID FD pair takes one line like below.
22487 27
22652 21
21846 33
21780 26
22476 14
21571 10
22740 17
21901 26
But if I use the for loop it outputs differently.
#!/bin/bash
for PidFd in `sudo ss -p | grep CLOSE-WAIT | awk '{ print $7 }' | sed 's/,pid=/ /' | sed 's/,fd=/ /' | sed 's/))//' | awk '{ print $2, $3 }'`
do
echo "$PidFd"
done
It adds extra newline between the two.
22487
27
22652
21
21846
33
21780
26
22476
14
21571
10
22740
17
21901
26
I would like to fix that to have the same output.
Solution
You get an extra newline because for
doesn't just split on newlines -- it also splits on spaces, so instead of getting a pid/fd pair assigned to PidFd
, it first has a PID assigned, and then has a FD. As extensively documented at DontReadLinesWithFor, the best-practice approach is to use a while read
loop instead.
Moreover, one doesn't need to use awk
, grep
, or sed
at all for what you're trying to accomplish here (although grep
can help performance on systems with a large number of sockets, at the expense of making it worse on systems with only a small number of sockets):
#!/usr/bin/env bash
case $BASH_VERSION in '') echo "ERROR: Your shell must be bash, not sh" >&2; exit 1;; esac
pid_re='pid=([[:digit:]]+)($|[^[:digit:]])'
fd_re='fd=([[:digit:]]+)($|[^[:digit:]])'
while IFS= read -r line; do pid=; fd=
[[ $line =~ $pid_re ]] && pid=${BASH_REMATCH[1]}
[[ $line =~ $fd_re ]] && fd=${BASH_REMATCH[1]}
[[ $pid && $fd ]] && echo "$pid $fd"
done < <(sudo ss -p | grep CLOSE-WAIT)
Answered By - Charles Duffy Answer Checked By - Gilberto Lyons (WPSolving Admin)