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 ]]
натыкаясь на пустую строку завершает процесс.