Search code examples
bashtelnetcat

Reading telnet delayed answer in bash


I've been trying to read (on raspbian OS) answer from telnet server using bash script nc or /dev/tcp/. But the server answers very slow, I have to wait ~3 seconds for the answer. Here is my working code, what I typed manually and I want to use it in automated bash script:

telnet 192.168.1.225 233
Trying 192.168.1.225...
Connected to 192.168.1.225.
Escape character is '^]'.
7
Voltage: 222.2, Current: 0.0 Power: 0, Energy: 176
7
Voltage: 222.0, Current: 0.0 Power: 0, Energy: 176
q
^]
telnet> quit
Connection closed

"7" is my command to server and "Voltage.." is the answer. So I googled, and tried the following bash script:

#!/bin/bash
exec 3<>/dev/tcp/192.168.1.225/233
echo "sending 7"
echo -en "7\n" >&3
sleep 20 #wait  enough time for answer
echo "waiting..."
#read -r RESPONSE <&3
#RESPONSE="$(cat <&3)"
#RESPONSE="${cat <&3}"
RESPONSE="`cat <&3`"
echo "waiting..."
echo "Response is: $RESPONSE"

As you can see I tried multiple ways, but none of them worked. When I execute the script I get this:

sending 7
waiting...
^C

^C-> The script does not answer, and I have to kill it using ctrl+c

I want to get the message and parse the numeric values. I think the problem is with cat, but I couldn't figure out, how to use it properly with delayed answer.

I tried execute my script in shell:

root@raspberrypi:/home/pi/gabor# exec 3<>/dev/tcp/192.168.1.225/233
root@raspberrypi:/home/pi/gabor# echo -en "7\n" >&3
root@raspberrypi:/home/pi/gabor# cat <&3
Voltage: 223.4, Current: 0.0 Power: 0, Energy: 175
^C

How can I get the answer?

Update1: I tried printf '7\n' >&3, no change. Update2: I waited more time and got the following result:

sending 7

20 secs after starting the script

waiting...

~3 minutes after starting the script

waiting...
Response is: Voltage: 226.4, Current: 0.0 Power: 0, Energy: 177

Solution

  • If it's a buffering issue, reading one character at a time may solve the problem.

    #!/bin/bash
    
    exec 3<>/dev/tcp/192.168.1.225/233
    echo 7 >&3
    IFS=''
    while read -u 3 -N 1 char; do
        [[ $char == $'\n' ]] && break
        RESPONSE+=$char
    done
    IFS=$' \t\n'
    exec 3>&-
    
    echo $RESPONSE
    

    The above uses the shell's builtin read to consume one character at a time, and breaks out of the loop if a newline is read. The IFS must not contain newline, given that the loop needs to check for it. After leaving the loop, IFS is reset and the connection closed.