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.
CodePudding user response:
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)
CodePudding user response:
As Charles commented, awk can do the whole pipeline
sudo ss -p | awk '
/CLOSE-WAIT/ {
field = $7
sub(/,pid=/, " ", field)
sub(/,fd=/, " ", field)
sub(/))/, "", field)
split(field, a)
print a[2], a[3]
}
'
The bash mapfile
command can read lines into an array:
mapfile -t lines < <(
sudo ss -p | awk '
/CLOSE-WAIT/ {
field = $7
sub(/,pid=/, " ", field)
sub(/,fd=/, " ", field)
sub(/))/, "", field)
split(field, a)
print a[2], a[3]
}
'
)
Now, you can do
for PidFd in "${lines[@]}"; do
echo "$PidFd"
done