Bash. Коммуникация с сокетом без curl, nc и telnet.

Bash. Коммуникация с сокетом без curl, nc и telnet.

caffeine_zz
Не много, не мало, просто решение одной досадной проблемы, эффективного решения для которой не нашлось в интернете.

Не редко возникает необходимость в общении с TCP/UDP сокетом из bash скрипта.
В этих случаях мы начинаем изощряться с curl, nc и telnet как-то нужно писать в сокет, как-то нужно читать из него. А если несколько скриптов то еще нужно умудриться прочитать свой ответ.

Но что делать если мы оказались в docker контейнере без curl, nc и telnet?

Вот например такая ситуация возникла у автора статьи при использовании telegram-cli в alpine:3.6.

Помочь нам может псевдоустройство /dev/tcp !
/dev/tcp поумолчанию не активен во многих docker имейджах. Исправляем это дело:

mknod /dev/tcp c 30 36

Теперь мы можем общаться с /dev/tcp

Поумолчанию telegram-cli в режиме daemon слушает 127.0.0.1:7313
Для того чтобы отправить что-то в этот сокет достаточно выполнить

echo -e "$your_var" > /dev/tcp/127.0.0.1/7313


Как писать - решили! А как читать будем?
Вот тут нам помогут дескрипторы!

Пишем скрипт (send.sh):

#!/bin/bash
mess=$@
send() {
   exec 3<>/dev/tcp/127.0.0.1/7313
   echo -e "$mess" >&3
   cat <&3
}
send
exit 0

Тут exec 3<>/dev/tcp/127.0.0.1/7313 открывает на чтение и запись
дескриптор 3 и присваивает ему псевдоустройство /dev/tcp/127.0.0.1/7313
Пишем в сокет с помощью   echo -e "$mess" >&3
А с помощью cat <&3 читаем ответ.

Теперь по команде ./send.sh $your_var мы можем передать в сокет что-либо
и прочитать ответ.

Но как всегда есть как минимум одно НО!
В нашем случае, это досадная проблема с stdin...
Дело в том, что cat <&3 это ничто иное как чтение stdin
Отсюда мы получаем текст ответа + пустую строку + проблему в виде зависшего процесса. У него просто нет команды выхода... И какие бы команды после cat <&3 мы не строили, до них процесс просто не дойдет. Остановить процесс можно только убив его из вне или нажав CTRL+C.

Врядли нам это подходит, правда!??
А что мы еще можем?

Мы можем читать из дескриптора построчно, с помощью read -u через цикл.

Нам нужно за что-то зацепиться, чтобы придумать условие успешного выхода из цикла.
А зацепиться мы можем за пустую строку, которую получаем из сокета в конце каждого ответа.

Делаем:

#!/bin/bash

mess=$@

send() {
   exec 3<>/dev/tcp/127.0.0.1/7313
   echo -e "$mess" >&3
   while read -u 3 -r line
   do
       echo $line
       if [[ -z $line ]]; then
           exit 0
       fi
   done
   exec 3>&-
}
send
exit 0

Вот теперь то по команде ./send.sh $your_var мы получаем текст ответа + пустую строку а наше условие [[ -z $line ]] натыкаясь на пустую строку завершает процесс.

Report Page