On Mon, Apr 7, 2025 at 5:37 PM A. James Lewis <ja...@fsck.co.uk> wrote:
>
> I have always attempted to avoid
> using external programs where functionality within bash can meet a
> requirement.  Doing this allows my scripts to be more reliable, and not
> depend on those external tools being installed...
>
> I have however found it extremely frustrating to open TCP connections
> via /dev/tcp, because there appears to be no way to control the
> timeout!  This means I cannot "try one server and move on to the next
> if it's not responding" etc... the default timeout is quite long, so
> even a simple script to check a list of servers for a response on a
> given port is problematic.
>
> I realise that the action can be performed in a subshell, with the use
> of "timeout", but to my knowledge, a file descriptor cannot be passed
> back from that subshell, which makes communicating with a remote system
> once the connection is open quite challenging/inconvenient.
>

How about doing the entire connection in a function? That would allow
using a file descriptor how you want to... even if it means only using
it to be quick and check if a server is hanging, if your script is
otherwise wanting to do some complex (time consuming) interaction with
the server.

If you are open to basic external commands like ps and sleep, the
below is a way to control the timeout with current bash functionality.

#### script begin

maxdur=50 # 5 seconds (x0.1)
port=80
method=GET path=/
#method=OPTIONS path='*'

hosts()
{
    cat <<-EOT
www.google.com
www.example.com
8.8.8.9
EOT
    # 8.8.8.8 is Google DNS
    # I made up the increment
}

chat()
{
    exec 3<> /dev/tcp/$host/$port
    cat >&3 <<-EOT
    $method $path HTTP/1.0
Host: $host
Connection: close
EOT
    # use &4 to bypass &1/2 output drop for bash errors
    cat <&3 | head -n 1 >&4
    exec 3<&-
}

hosts |
while read host; do
    # drop bash errors on eventual timeout
    chat 4>&1 > /dev/null 2>&1 &
    dur=0
    while ((dur<maxdur)) && ps -p $! > /dev/null; do
        sleep 0.1
       ((dur+=1))
       (((dur%10)==0)) &&
       echo "sleeping... waiting on $host"
    done
    # kill the connection/chat if it's still trying
    ps -p $! > /dev/null && kill $!
    echo "Done with: $host"
done

#### script end

ps. You can put tabs before lines within the heredocs, including the
end EOT lines, but the tab character doesn't paste into gmail
webmail... :/

Reply via email to