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?

0

    3 Answers 3

    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
      • 2
        Don't use a separate grep command when you can add the filter to awk: awk '/SSID:|signal/ {printf ...}'
        – chepner
        CommentedMay 23, 2015 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
        CommentedMay 23, 2015 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).

        You must log in to answer this question.

        Start asking to get answers

        Find the answer to your question by asking.

        Ask question

        Explore related questions

        See similar questions with these tags.