Проверьте, открыт ли удаленный порт TCP из сценария оболочки

298

Я ищу быстрый и простой способ для правильного тестирования, если данный порт TCP открыт на удаленном сервере, из сценария Shell.

Мне удалось сделать это с помощью команды telnet, и она прекрасно работает, когда порт открыт, но, похоже, не истекает время ожидания, когда это не так, и просто висит там ...

Вот пример:

l_TELNET=`echo "quit" | telnet $SERVER $PORT | grep "Escape character is"`
if [ "$?" -ne 0 ]; then
  echo "Connection to $SERVER on port $PORT failed"
  exit 1
else
  echo "Connection to $SERVER on port $PORT succeeded"
  exit 0
fi

Мне либо нужен лучший способ, либо способ заставить telnet прервать тайм-аут, например, если он не подключится менее чем за 8 секунд, и вернуть то, что я могу перехватить в Shell (код возврата или строку в stdout).

Я знаю метод Perl, который использует модуль IO :: Socket :: INET и написал успешный скрипт, который тестирует порт, но хотел бы по возможности избегать использования Perl.

Примечание: это то, что мой сервер работает (откуда мне нужно запустить это)

SunOS 5.10 Generic_139556-08 i86pc i386 i86pc

Яник Жируард
источник
Что насчет Netcat или Nmap ?
Иеремия Уиллкок
Ответ соврал с Expect. Мы написали простой скрипт, который отправляет телнет на нужный порт с тайм-аутом 8 секунд. Есть много примеров, чтобы выбрать из тоже. Мы основаны наши прочь этот пост: unix.com/shell-programming-scripting/...
Яник Жируард
1
check_tcp с github.com/monitoring-plugins/monitoring-plugins может сделать это, включая ввод строк и проверку на ожидаемый ответ.

Ответы:

452

Как указал Б.Роудс, ncсделаем всю работу. Более компактный способ его использования:

nc -z <host> <port>

Этот способ ncбудет проверять только, открыт ли порт, выход с 0 при успехе, 1 при ошибке.

Для быстрой интерактивной проверки (с таймаутом 5 секунд):

nc -z -v -w5 <host> <port>
Алессио Гаэта
источник
48
Centos7 по умолчанию использует nmap netcat и не имеет опции -z.
Jolestar
7
Это не работает в RHEL / Centos. Для этих дистрибутивов вам необходимо: nc -vn <хост> <порт>
Джастин Деннауэр
2
FWIW, я полностью пересмотрел свой ответ с примером , отдельно применимым как к RHEL 6, так и к RHEL 7.
Acumenus
4
по крайней мере, на Mac вам может потребоваться добавить, -G#чтобы установить тайм-аут соединения отдельно от / в дополнение к -w#тайм-ауту, который в основном функционирует как тайм-аут чтения.
Доктор
1
@jolestar Вы можете вручную обновить Ncat на Centos 7, чтобы получить -zвозможность. Возможно, вы захотите рассмотреть вопрос: unix.stackexchange.com/questions/393762/…
Дамьен Ó Силай
150

Это достаточно легко сделать с -zи -w TIMEOUTопциями nc, но не все системы ncустановлены. Если у вас достаточно свежая версия bash, это будет работать:

# Connection successful:
$ timeout 1 bash -c 'cat < /dev/null > /dev/tcp/google.com/80'
$ echo $?
0

# Connection failure prior to the timeout
$ timeout 1 bash -c 'cat < /dev/null > /dev/tcp/sfsfdfdff.com/80'
bash: sfsfdfdff.com: Name or service not known
bash: /dev/tcp/sfsfdfdff.com/80: Invalid argument
$ echo $?
1

# Connection not established by the timeout
$ timeout 1 bash -c 'cat < /dev/null > /dev/tcp/google.com/81'
$ echo $?
124

Здесь происходит то timeout, что подкоманда будет запускаться и уничтожать ее, если она не завершится в течение указанного времени (1 секунда в приведенном выше примере). В этом случае bashподкоманда использует специальную обработку / dev / tcp, чтобы попытаться открыть соединение с указанным сервером и портом. Если вы bashможете открыть соединение в течение тайм-аута, catпросто закройте его немедленно (так как оно читает из /dev/null) и выйдите с кодом состояния, 0который будет распространяться до, bashа затем timeout. Если получен bashсбой соединения до истечения указанного времени ожидания, bashпроизойдет выход с кодом выхода 1, который timeoutтакже вернется. И если bash не может установить соединение и истекает указанное время ожидания, тоtimeoutубьет bashи выйдет со статусом 124.

onlynone
источник
1
Есть /dev/tcpдоступны на других системах , чем Linux? А как насчет Mac в частности?
Питер
8
Параметр / dev / tcp является функцией bash, так что да. Однако похоже, что у Макса нет тайм-аута ...
нет
1
@onlynone - похоже, timeoutтакже не существует во FreeBSD, и на моем старом Ubuntu-пакете это устанавливаемый пакет, но по умолчанию его там нет. Было бы здорово, если бы был способ сделать это только в bash, без сторонних инструментов, таких как timeout или netcat.
Грэм
3
Просто хотелось бы отметить , что , как timeoutпредставляется , быть частью GNU Coreutils, и может быть установлен на Маках с доморощенного: brew install coreutils. Затем он будет доступен как gtimeout.
только
1
@Wildcard Журнал изменений bash предлагает bash-2.04-devel, хотя поддержка имен хостов могла быть добавлена ​​в bash-2.05-alpha1.
Acumenus
109

TOC:

  • Использование Bash и timeout
    • команда
    • Примеры
  • С помощью nc
    • команда
    • RHEL 6 (nc-1,84)
      • Монтаж
      • Примеры
    • RHEL 7 (nmap-ncat-6.40)
      • Монтаж
      • Примеры
  • замечания

Используя bash и timeout:

Обратите внимание, что он timeoutдолжен присутствовать в RHEL 6+ или альтернативно находится в GNU coreutils 8.22. В MacOS установите его используя brew install coreutilsи используйте как gtimeout.

Команда:

$ timeout $TIMEOUT_SECONDS bash -c "</dev/tcp/${HOST}/${PORT}"; echo $?

При параметризации хоста и порта обязательно указывайте их как ${HOST}и ${PORT}выше. Не указывайте их просто как $HOSTи $PORT, т.е. без скобок; это не сработает в этом случае.

Пример:

Успех:

$ timeout 2 bash -c "</dev/tcp/canyouseeme.org/80"; echo $?
0

Неудача:

$ timeout 2 bash -c "</dev/tcp/canyouseeme.org/81"; echo $?
124

Если вы должны сохранить статус выхода bash,

$ timeout --preserve-status 2 bash -c "</dev/tcp/canyouseeme.org/81"; echo $?
143

Использование nc:

Обратите внимание, что ncна RHEL 7 устанавливается обратная несовместимая версия .

Команда:

Обратите внимание, что приведенная ниже команда уникальна в том смысле, что она идентична для RHEL 6 и 7. Отличаются только установка и вывод.

$ nc -w $TIMEOUT_SECONDS -v $HOST $PORT </dev/null; echo $?

RHEL 6 (nc-1,84):

Монтаж:

$ sudo yum install nc

Примеры:

Успех:
$ nc -w 2 -v canyouseeme.org 80 </dev/null; echo $?
Connection to canyouseeme.org 80 port [tcp/http] succeeded!
0
Неудача:
$ nc -w 2 -v canyouseeme.org 81 </dev/null; echo $?
nc: connect to canyouseeme.org port 81 (tcp) timed out: Operation now in progress
1

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

$ nc -w 2 -v microsoft.com 81 </dev/null; echo $?
nc: connect to microsoft.com port 81 (tcp) timed out: Operation now in progress
nc: connect to microsoft.com port 81 (tcp) timed out: Operation now in progress
nc: connect to microsoft.com port 81 (tcp) timed out: Operation now in progress
nc: connect to microsoft.com port 81 (tcp) timed out: Operation now in progress
nc: connect to microsoft.com port 81 (tcp) timed out: Operation now in progress
1

RHEL 7 (nmap-ncat-6.40):

Монтаж:

$ sudo yum install nmap-ncat

Примеры:

Успех:
$ nc -w 2 -v canyouseeme.org 80 </dev/null; echo $?
Ncat: Version 6.40 ( http://nmap.org/ncat )
Ncat: Connected to 52.202.215.126:80.
Ncat: 0 bytes sent, 0 bytes received in 0.22 seconds.
0
Неудача:
$ nc -w 2 -v canyouseeme.org 81 </dev/null; echo $?
Ncat: Version 6.40 ( http://nmap.org/ncat )
Ncat: Connection timed out.
1

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

$ nc -w 2 -v microsoft.com 81 </dev/null; echo $?
Ncat: Version 6.40 ( http://nmap.org/ncat )
Ncat: Connection to 104.43.195.251 failed: Connection timed out.
Ncat: Trying next address...
Ncat: Connection to 23.100.122.175 failed: Connection timed out.
Ncat: Trying next address...
Ncat: Connection to 23.96.52.53 failed: Connection timed out.
Ncat: Trying next address...
Ncat: Connection to 191.239.213.197 failed: Connection timed out.
Ncat: Trying next address...
Ncat: Connection timed out.
1

Примечания:

-v( --verbose) Аргумент и echo $?команда, конечно , только для иллюстрации.

Акаменус
источник
3
timeout 2 bash -c "</dev/tcp/canyouseeme.org/81"; echo $?это отличный момент!
Ipeacocks
добавить 2>&1к не эхо-статус result_test=$(nc -w 2 $ip_addreess 80 </dev/null 2>&1 ; echo $?)
Sanya Snex
56

С помощью этого netcatвы можете проверить, открыт ли порт следующим образом:

nc my.example.com 80 < /dev/null

Возвращаемое значение ncбудет успешным, если был открыт порт TCP, и ошибкой (обычно код возврата 1), если не удалось установить соединение TCP.

Некоторые версии ncбудут зависать при попытке этого, потому что они не закрывают отправляющую половину своего сокета даже после получения конца файла /dev/null. На моем собственном ноутбуке Ubuntu (18.04) netcat-openbsdустановленная мной версия netcat предлагает обходной путь: -Nдля получения немедленного результата необходима опция:

nc -N my.example.com 80 < /dev/null
Брэндон Роудс
источник
1
Прекрасно работает для вкусов, ncкоторые не поддерживают -wфлаг.
Дэвид Доссо
5
спасибо - также работает для версий nc без -zподдержки - работает на RHEL / CentOS 7, например
Andrew
1
Несмотря на то, что этот подход хорош, -wон необходим, в противном случае команда при использовании в сценарии может зависать более десяти секунд. Я написал ответ, который включает в себя -w.
Acumenus
2
+1 это здорово, потому что nc стандартно поставляется с альпийским linux и ubuntu. наверное другие. нет необходимости устанавливать дополнительные, когда вы находитесь в отдалении и не можете. Спасибо за это! Я мог бы добавить,nc host port -w 2 && echo it works
std''OrgnlDave
2
Я предпочитаю nc -vz localhost 3306. Это дает более подробный вывод. Пробовал только на Mac.
EFreak
29

В Bash использование файлов псевдоустройств для соединений TCP / UDP просто. Вот сценарий:

#!/usr/bin/env bash
SERVER=example.com
PORT=80
</dev/tcp/$SERVER/$PORT
if [ "$?" -ne 0 ]; then
  echo "Connection to $SERVER on port $PORT failed"
  exit 1
else
  echo "Connection to $SERVER on port $PORT succeeded"
  exit 0
fi

Тестирование:

$ ./test.sh 
Connection to example.com on port 80 succeeded

Вот одна строка (синтаксис Bash):

</dev/tcp/localhost/11211 && echo Port open. || echo Port closed.

Обратите внимание, что некоторые серверы могут быть защищены брандмауэром от SYN-атак, поэтому может возникнуть тайм-аут соединения TCP (~ 75 сек). Чтобы обойти проблему тайм-аута, попробуйте:

timeout 1 bash -c "</dev/tcp/stackoverflow.com/81" && echo Port open. || echo Port closed.

См .: Как уменьшить время ожидания системного вызова TCP connect ()?

kenorb
источник
1
@ABB Это связано с настройкой брандмауэра сервера, который не дает никакого ответа для предотвращения любых атак SYN-флуд. Бег telnet canyouseeme.org 81тоже зависает. Это контролируется вашими пределами тайм-аута, которые, вероятно, жестко закодированы в bash. См .: уменьшить время ожидания системного вызова TCP connect () .
Кенорб
Для параметризации теперь представляется необходимым указать $SERVERи $PORTс фигурными скобками, по крайней мере, как ${SERVER}и ${PORT}.
Acumenus
13

Мне нужно было более гибкое решение для работы с несколькими git-репозиториями, поэтому я написал следующий sh-код, основанный на 1 и 2 . Вы можете использовать свой адрес сервера вместо gitlab.com и ваш порт вместо 22.

SERVER=gitlab.com
PORT=22
nc -z -v -w5 $SERVER $PORT
result1=$?

#Do whatever you want

if [  "$result1" != 0 ]; then
  echo  'port 22 is closed'
else
  echo 'port 22 is open'
fi
Ахмад Юсофан
источник
1
Спасибо! Это облегчает мне жизнь /etc/rc.local
Шахин Халед
10

Если вы используете ksh или bash, они оба поддерживают перенаправление ввода-вывода в / из сокета с помощью конструкции / dev / tcp / IP / PORT . В этом Korn оболочки примера я перенаправлять не-оп - х ( : ) не Std-в из сокета:

W$ python -m SimpleHTTPServer &
[1]     16833
Serving HTTP on 0.0.0.0 port 8000 ...
W$ : </dev/tcp/127.0.0.1/8000

Оболочка печатает ошибку, если сокет не открыт:

W$ : </dev/tcp/127.0.0.1/8001
ksh: /dev/tcp/127.0.0.1/8001: cannot open [Connection refused]

Поэтому вы можете использовать это как тест в условии if :

SERVER=127.0.0.1 PORT=8000
if (: < /dev/tcp/$SERVER/$PORT) 2>/dev/null
then
    print succeeded
else
    print failed
fi

No-op находится в подоболочке, поэтому я могу выбросить std-err, если перенаправление std-in завершится неудачно.

Я часто использую / dev / tcp для проверки доступности ресурса по HTTP:

W$ print arghhh > grr.html
W$ python -m SimpleHTTPServer &
[1]     16863
Serving HTTP on 0.0.0.0 port 8000 ...
W$ (print -u9 'GET /grr.html HTTP/1.0\n';cat <&9) 9<>/dev/tcp/127.0.0.1/8000
HTTP/1.0 200 OK
Server: SimpleHTTP/0.6 Python/2.6.1
Date: Thu, 14 Feb 2013 12:56:29 GMT
Content-type: text/html
Content-Length: 7
Last-Modified: Thu, 14 Feb 2013 12:55:44 GMT

arghhh
W$ 

Этот однострочник открывает файловый дескриптор 9 для чтения и записи в сокет, печатает HTTP GET в сокет и использует его catдля чтения из сокета.

merebagatelle
источник
1
@ABB Я думаю, вы имеете в виду, что он долго зависает, если порт не отвечает, например, когда фильтруется, или на IP ничего нет. Закрытый порт не вызывает задержки, если порт активно отказывает в соединении. Для многих целей это вполне приемлемо.
mc0e
Эта техника уже была опубликована перед этим ответом в предыдущем ответе kenorb . В любом случае, более важным является то, что он долго зависает, если порт не отвечает. Например, попробуйте </dev/tcp/canyouseeme.org/80и тогда </dev/tcp/canyouseeme.org/81.
Acumenus
9

Хотя это старый вопрос, я только что рассмотрел его вариант, но ни одно из решений здесь не применимо, поэтому я нашел другое и добавляю его для потомков. Да, я знаю, что ОП сказал, что они знали об этой опции, и она им не подходила, но для любого, кто последует после этого, она может оказаться полезной.

В моем случае я хочу проверить наличие локальной apt-cacher-ngслужбы из dockerсборки. Это означает, что абсолютно ничего нельзя установить до теста. Нет nc, nmap, expect, telnetили python. perlоднако присутствует вместе с основными библиотеками, поэтому я использовал это:

perl -MIO::Socket::INET -e 'exit(! defined( IO::Socket::INET->new("172.17.42.1:3142")))'
mc0e
источник
7

проверить порты используя bash

пример

$ ./test_port_bash.sh 192.168.7.7 22

порт 22 открыт

Код

HOST=$1
PORT=$2
exec 3> /dev/tcp/${HOST}/${PORT}
if [ $? -eq 0 ];then echo "the port $2 is open";else echo "the port $2 is closed";fi
Krbu
источник
6

В некоторых случаях, когда такие инструменты, как curl, telnet, nc o nmap недоступны, у вас все еще есть шанс с помощью wget

if [[ $(wget -q -t 1 --spider --dns-timeout 3 --connect-timeout 10  host:port; echo $?) -eq 0 ]]; then echo "OK"; else echo "FAIL"; fi
PRF
источник
4

Если вы хотите использовать, ncно не поддерживает версию -z, попробуйте использовать --send-only:

nc --send-only <IP> <PORT> </dev/null

и с таймаутом:

nc -w 1 --send-only <IP> <PORT> </dev/null

и без поиска DNS, если это IP:

nc -n -w 1 --send-only <IP> <PORT> </dev/null

Он возвращает коды в -zзависимости от того, может ли он подключиться или нет.

Крис Мендес
источник
1

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

Как насчет того, чтобы поместить его в цикл while с таймером? Я скорее Perl, чем Solaris, но в зависимости от используемой вами оболочки, вы сможете сделать что-то вроде:

TIME = 'date +%s' + 15
while TIME != `date +%s'
do whatever

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

Я подозреваю, что у telnet также есть переключатель тайм-аута, но я думаю, что вышесказанное сработает.

Красный томас
источник
1

Мне нужен был короткий скрипт, который запускался в cron и не выводил. Я решаю свои проблемы с помощью Nmap

open=`nmap -p $PORT $SERVER | grep "$PORT" | grep open`
if [ -z "$open" ]; then
  echo "Connection to $SERVER on port $PORT failed"
  exit 1
else
  echo "Connection to $SERVER on port $PORT succeeded"
  exit 0
fi

Для его запуска Вам следует установить nmap, поскольку он не является установленным по умолчанию пакетом.

Кристиан Куласа
источник
nmapgrepable вывод также может быть полезен -oG -для отправки его на стандартный вывод. (grep потребовалось бы немного модифицировать)
Герт ван ден Берг
0

Это использует telnet за кулисами, и, кажется, отлично работает на Mac / Linux. Он не использует netcat из-за различий между версиями в linux / mac, и это работает с установкой Mac по умолчанию.

Пример:

$ is_port_open.sh 80 google.com
OPEN

$ is_port_open.sh 8080 google.com
CLOSED

is_port_open.sh

PORT=$1
HOST=$2
TIMEOUT_IN_SEC=${3:-1}
VALUE_IF_OPEN=${4:-"OPEN"}
VALUE_IF_CLOSED=${5:-"CLOSED"}

function eztern()
{
  if [ "$1" == "$2" ]
  then
    echo $3
  else
    echo $4
  fi
}

# cross platform timeout util to support mac mostly
# https://gist.github.com/jaytaylor/6527607
function eztimeout() { perl -e 'alarm shift; exec @ARGV' "$@"; }

function testPort()
{
  OPTS=""

  # find out if port is open using telnet
  # by saving telnet output to temporary file
  # and looking for "Escape character" response
  # from telnet
  FILENAME="/tmp/__port_check_$(uuidgen)"
  RESULT=$(eztimeout $TIMEOUT_IN_SEC telnet $HOST $PORT &> $FILENAME; cat $FILENAME | tail -n1)
  rm -f $FILENAME;
  SUCCESS=$(eztern "$RESULT" "Escape character is '^]'." "$VALUE_IF_OPEN" "$VALUE_IF_CLOSED")

  echo "$SUCCESS"
}

testPort 
Брэд Паркс
источник
0

Основываясь на ответе, получившем наибольшее количество голосов, здесь есть функция ожидания открытия двух портов, а также время ожидания. Обратите внимание на два порта, которые должны быть открыты, 8890 и 1111, а также max_attempts (1 в секунду).

function wait_for_server_to_boot()
{
    echo "Waiting for server to boot up..."
    attempts=0
    max_attempts=30
    while ( nc 127.0.0.1 8890 < /dev/null || nc 127.0.0.1 1111 < /dev/null )  && [[ $attempts < $max_attempts ]] ; do
        attempts=$((attempts+1))
        sleep 1;
        echo "waiting... (${attempts}/${max_attempts})"
    done
}
Жоао Роша да Силва
источник
-1

nmap-ncat для проверки локального порта, который еще не используется


availabletobindon() {
  port="$1"
  nc -w 2 -i 1 localhost "$port" 2>&1 | grep -v -q 'Idle timeout expired'
  return "$?"
}
isleofgray
источник
Это не говорит мне, что мой порт 80 открыт.
Тоторо