2

I'm trying to automatically query a status via telnet (this isn't something I'm able to work around in this case). The idea is to grep for something and assign the result to a variable, to pass to a conditional statement later. The complication is the link to the target device may not be up, and therefore the script could hang indefinitely if not killed via a timeout.

This sets output to what I'd expect, but does not handle the link being down:

output=$(telnet 1.2.3.4 1234 | grep "something")

This will spit out the expected output while handling the link being down:

timeout --signal=9 3 telnet 1.2.3.4 1234 | grep "something"

I can even direct the output to a file and the file will contain the output:

timeout --signal=9 3 telnet 1.2.3.4 1234 | grep "something" > /tmp/tmpfile.txt

Unfortunately rapid write/read of a file like this isn't an option because of how much it'll fill up log files.

But, when I try to combine everything, the variable doesn't set:

output=$(timeout --signal=9 3 telnet 1.2.3.4 1234 | grep "something")

Or, rather, it sets it to a blank value, because if I set it before running the above, the variable is blank afterwards.

1

telnet expects a tty on its stdin, but timeout takes that away.

If you really insist on using telnet you may do so by adding --foreground option to timeout, as in:

output=$(timeout --foreground --signal=9 3 telnet 1.2.3.4 1234 2>&1 | grep "something")

Besides, if you can have nc on your system you should really rather use that for your purpose:

output=$(timeout 3 nc 1.2.3.4 1234 | grep "something")

If neither nc nor timeout --foreground are an option for you then you really need an alternative to telnet that won’t need a tty.

I see you tagged your question bash, so you could use Bash’s own networking facilities, and thus your line might become like:

output=$(timeout 3 cat < /dev/tcp/1.2.3.4/1234 | grep "something")

If not even cat is an option then you could replace it with a one-liner script in Bash, like in:

output=$(timeout 3 stdbuf -oL bash -c 'while read line ; do echo "${line}" ; done < /dev/tcp/1.2.3.4/1234 | grep "something"')

Hoping that at least stdbuf (which is part of standard coreutils package) is available in your system.

In this last alternative however pay attention to your grep regex: if you have single-quotes in there then you need to escape them by first quitting the main single-quote pair.

That is needed also if you need to pass variables (eg hostname and/or port number) from your shell to the one-liner script. For instance:

hostname=1.2.3.4
portnumber=1234

output=$(timeout 3 stdbuf -oL bash -c 'while read line ; do echo "${line}" ; done < /dev/tcp/'"${hostname}"'/'"${portnumber}"' | grep "something"')

Here I'm assuming that ${hostname} and ${portnumber} values can be trusted, ie provided by you or by other trusted sources that won't give illegitimate, invalid, or dangerous values.

  • Unfortunately, I don't have nc and my timeout also lacks --foreground (and anything other than -s or --signal). – Ken Oh Mar 27 at 5:14
  • @KenOh Having noted the bash tag in your question I’ve updated my answer for more alternatives , but if bash is actually not an option for you then please specify what system you are in – LL3 Mar 27 at 12:12
  • Sorry, I should have noted that it's on RHEL 6, with the complication that a lot of the utility commands being bare bones, and I'm not able to change any of that. I really appreciate the one-liner suggestions. They actually set the variable where the timeout versions do not, however they hang forever if the the link is down. My original thought was to send it to the background via &, but that failed when I couldn't pull variables from the background process to the current script. I may be barking up the wrong tree and should use a subscript with exit codes or something. – Ken Oh Mar 27 at 14:56
  • 1
    @KenOh Oh right, sorry, I had forgotten about that requirement! I’ve updated my answer again for the one-liner script, making it use timeout again in place of the -t 3 option of read which would timeout only after the connection was at least established. Anyway, besides the one-liner script, doesn’t RHEL6 even has cat ?! – LL3 Mar 27 at 16:54
  • I'm marking this as answered. Thank you very much! I do have to figure out why I'm having problems inserting variables into my telnet targets (e.g. /dev/tcp/$ip/$port seems to not pass the variables), but I suppose that's a different question. – Ken Oh Mar 27 at 17:31
1

It is the standard error that is not being redirected (when your link is down)! This will solve your problem, because it redirects the standard error to the standard output.

output=$(timeout --signal=9 3 telnet 1.2.3.4 1234 2>&1 | grep "something") 
  • I meant to say I've tried that. Puzzlingly, it doesn't change the behavior one bit (even if I output it to a file). To be clear, I don't need stderr. I actually do want what is being grepped. – Ken Oh Mar 26 at 18:14
  • Can you post a example of what are being returned? I tested here, and it worked fine... Are you really running bash? – Luciano Andress Martini Mar 26 at 18:16
  • Sure! timeout --signal=9 3 telnet 1.2.3.4 1234 2>&1 | grep "something" just returns something while timeout --signal=9 3 telnet 1.2.3.4 1234 | grep "something" returns something Connection Closed by foreign host. – Ken Oh Mar 26 at 19:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.