How to avoid piping awk to awk?

Q23

I have written the following script, which runs from my user's crontab, parses ~/.ssh/config for SSH port forwards, and automatically connects them if the Host line ends with "remote," "local," or "dynamic."

Example ~/.ssh/config blocks:

Host localsocksdynamic
     DynamicForward 8080
     Hostname 1.2.3.4

Host localwebserverremote
     RemoteForward *:8080 localhost:80
     Hostname 1.2.3.4

Host indirectaccesslocal
     LocalForward 2222 2.3.4.5:80
     Hostname 1.2.3.4

Script:

#!/bin/sh

port_forwards=$(awk '/Host .*(remote|local|dynamic)/{printf $2} " "}' ~/.ssh/config  
for forward in ${port_forwards}
do
     port=$(awk "/${forward}/{f=1;next}/Host /{f=0}f" ~/.ssh/config | awk '/(Remote|Local|Dynamic)Forward/{print $2}' | cut -d':' -f2)
     [ -z ${port} ] || nc -z 127.0.0.1 ${port} || ssh -fqN ${forward} > /dev/null 2>&1
done

It works fine, but I was wondering whether it was possible to combine the awk commands in the port= line so that it's not necessary to pipe one awk to another. After a few hours of playing around and trying to understand the syntax I'm just not grokking it.

I know autossh exists and is a better way to do what I'm doing, but this is a learning experience and attempt to accomplish the same task without installing a package.

Stephen Kitt

You can perform all the text processing with a single AWK invocation, simplified since you only care about the forwarding:

#!/bin/sh

awk '/^Host / { host = $2 } host && /(Remote|Local|Dynamic)Forward/ { port=$2; gsub(".*:", "", port); print host, port }' ~/.ssh/config | while read host port; do
    nc -z 127.0.0.1 "${port}" || ssh -fqN "${host}" > /dev/null 2>&1
done

Written out more legibly, the AWK script is

/^Host / { host = $2 }

host && /(Remote|Local|Dynamic)Forward/ {
    port=$2; gsub(".*:", "", port); print host, port
}

This stores the host whenever it sees a “Host” statement, then on lines encountered while a valid host is defined, and matching one of the accepted “Forward” statements, retrieves the target port, drops the part before a colon if any, and outputs the host and port:

localsocksdynamic 8080
localwebserverremote 8080
indirectaccesslocal 2222

This is then fed into a while read loop to check the port and run SSH.

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related