2

I'm using the following command in a script to get synthetic wifi info:

echo "$(
    iw dev wlp1s0 link |
    grep '^\s*SSID:\s' |
    sed -r 's/^\s*SSID:\s//'
) $(
    iw dev wlp1s0 link |
    grep '^\s*signal:\s' |
    sed -r 's/^\s*signal:\s//'
)"

It works, and the output is like:

MySSID -46 dBm

However, it feels stupid to make the same call to iw dev wlp1s0 link twice, when I know that the result is going to be the same. Also, I would like to avoid having to store the output from iw in a temporary variable.

Is there something I can do, maybe using tee, to "duplicate" the output from iw to two jobs and then concatenate the results?

2

There's nothing wrong with using a temporary variable: variables are made to store data to be used several times.

In this case, you can combine the two grep/sed calls into a single call to GNU grep, sed or awk. It's easy with sed: pass the -n option to output only explicitly printed lines, and print the lines on which a substitution is made.

echo "$(
    iw dev wlp1s0 link |
    sed -nr -e 's/^\s*SSID:\s//p' -e 's/^\s*signal:\s//'
)"

There is one difference from your original script: the SSID and signal values are printed in the order they appear in the output of iw. If you wanted to be independent of that order, it would be cumbersome with sed, so awk would be the tool of choice. With this approach, it's just as easy to get the output on a single line. The following script prints the last value for each setting (iw only outputs one, so in this specific case it doesn't matter); change e.g. ssid = $0 to ssid = ssid " " $0 to print them all.

iw dev wlp1s0 link | awk '
    $1=="SSID:" {sub(/[^:]*:[[:space:]]*/,""); ssid = $0}
    $1=="signal:" {sub(/[^:]*:[[:space:]]*/,""); signal = $0}
    END {print ssid, signal}
'

In general, if you want to send the output of a command to two different filters, you can use tee and pass it a process substitution. This is a bash feature (from ksh93, also present in zsh, but not in plain sh) that generalizes pipes. The tee command sees a file name that designates a pipe which is connected to the specified command.

iw dev wlp1s0 link | tee >(
    grep '^\s*SSID:\s' |
    sed -r 's/^\s*SSID:\s//'
  ) |
    grep '^\s*signal:\s' |
    sed -r 's/^\s*signal:\s//'

A limitation of this approach is that the command in the process substitution runs in its own subshell. You can't get variables back from it or control how its output is interspersed with others.

1

Here's a one liner of the iw command that have the same output as you.

iw dev wlp1s0 link | grep 'SSID:\|signal' | awk '{printf "%s ", $2$3}'

My output:

ZyXEL-AP-2,4GHz -46dBm
  • 2
    Don't use a separate grep command when you can add the filter to awk: awk '/SSID:|signal/ {printf ...}' – chepner May 23 '15 at 15:07
  • Oh I didn't know you could use awk like that. That's way more convenient, thanks for the advice @chepner. – krt May 23 '15 at 15:13
0

Something like that ?

iw dev wlp1s0 link |
grep -E '^\s*(SSID|signal):\s' |
sed -r 's/^\s*(SSID|signal):\s//' |
awk '{printf $0}'

grep will accept (SSID|signal): so it will match both SSID: and signal:.

-E is optional, but if you don't use it, think about escaping special meaning character. Here, it would be \(SSID\|signal\):

The same regex can be used for the sed part.

Finally, use awk to remove all trailing newlines (else, the output is displayed on 2 lines).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.