Yes, this is how I am doing it currently, but remember that once you
cut that subshell loose, you cannot easily get a response from it and 
certainly can't pass variables back... and you are more or less forced
to wait for a sensible timeout, even tho the actual connection might
take a much shorter time.

It also makes it extremely complex compared to a simple timeout
feature, where multiple data could be returned from the function in a
shorter time, and the file descriptor preserved for reuse after some of
that data is processed.

I do appreciate that these workarounds exist, but they are complex, and
limiting, compared to a simple TCP_TMOUT parameter or similar....  used
similarly to "IFS"... which would make this a much more usable tool.

James


On Tue, 2025-04-08 at 08:54 -0500, MacBeth wrote:
> 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