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... :/