Pruebe si el puerto TCP remoto está abierto desde un script de shell

297

Estoy buscando un método rápido y simple para probar adecuadamente si un puerto TCP determinado está abierto en un servidor remoto, desde dentro de un script de Shell.

Me las arreglé para hacerlo con el comando telnet, y funciona bien cuando se abre el puerto, pero parece que no se agota el tiempo de espera cuando no lo está y simplemente se cuelga allí ...

Aquí hay una muestra:

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

Necesito una mejor manera, o una forma de forzar el tiempo de espera de telnet si no se conecta en menos de 8 segundos, por ejemplo, y devolver algo que pueda atrapar en Shell (código de retorno o cadena en stdout).

Conozco el método Perl, que usa el módulo IO :: Socket :: INET y escribió un script exitoso que prueba un puerto, pero preferiría evitar usar Perl si es posible.

Nota: Esto es lo que está ejecutando mi servidor (desde donde necesito ejecutar esto)

SunOS 5.10 Generic_139556-08 i86pc i386 i86pc

Yanick Girouard
fuente
¿Qué pasa con Netcat o Nmap ?
Jeremiah Willcock
La respuesta mintió con esperar. Escribimos un script simple que envía un telnet en el puerto que necesitábamos, con un tiempo de espera de 8 segundos. También hay muchos ejemplos para elegir. Basamos el nuestro en esta publicación: unix.com/shell-programming-scripting/…
Yanick Girouard
1
check_tcp de github.com/monitoring-plugins/monitoring-plugins puede hacer esto, incluso ingresar cadenas y verificar la respuesta esperada.

Respuestas:

452

Como señaló B. Rhodes, nchará el trabajo. Una forma más compacta de usarlo:

nc -z <host> <port>

De esa manera ncsolo verificará si el puerto está abierto, saliendo con 0 en caso de éxito, 1 en caso de error.

Para una verificación interactiva rápida (con un tiempo de espera de 5 segundos):

nc -z -v -w5 <host> <port>
Alessio Gaeta
fuente
48
Centos7 usa por defecto nmap netcat y no tiene la opción -z.
Jolestar
77
Esto no funciona en RHEL / Centos. Para esas distribuciones necesita: nc -vn <host> <port>
Justin Dennahower
2
FWIW, he revisado completamente mi respuesta con un ejemplo , aplicable por separado tanto a RHEL 6 como a RHEL 7.
Acumenus
44
al menos en Mac, es posible que deba agregar -G#para establecer un tiempo de espera de conexión separado de / además del -w#tiempo de espera, que básicamente funciona como un tiempo de espera de lectura.
Doktor J
1
@jolestar Puede actualizar manualmente Ncat en Centos 7 para obtener la -zopción. Es posible que desee considerar: unix.stackexchange.com/questions/393762/…
Damien Ó Ceallaigh el
150

Es bastante fácil de hacer con los -zy -w TIMEOUTlas opciones a nc, pero no todos los sistemas se han ncinstalado. Si tiene una versión de bash lo suficientemente reciente, esto funcionará:

# 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

Lo que sucede aquí es que timeoutejecutará el subcomando y lo matará si no sale dentro del tiempo de espera especificado (1 segundo en el ejemplo anterior). En este caso, bashes el subcomando y utiliza su manejo especial / dev / tcp para intentar abrir una conexión al servidor y al puerto especificados. Si bashpuede abrir la conexión dentro del tiempo de espera, catsimplemente la cerrará inmediatamente (ya que está leyendo /dev/null) y 0saldrá con un código de estado del cual se propagará bashy luego timeout. Si se bashproduce un error de conexión antes del tiempo de espera especificado, bashsaldrá con un código de salida de 1 que timeouttambién regresará. Y si bash no puede establecer una conexión y el tiempo de espera especificado expira, entoncestimeoutmatará bashy saldrá con un estado de 124.

solo nadie
fuente
1
¿Está /dev/tcpdisponible en sistemas que no sean Linux? ¿Qué pasa con las Mac en particular?
Peter
8
Lo / dev / tcp es una característica de bash, así que sí. Sin embargo, parece que las Mac no tienen tiempo de espera ...
solo
1
@onlynone - Parece timeoutque tampoco existe en FreeBSD, y en mi antiguo cuadro de Ubuntu es un paquete instalable, pero no está allí por defecto. Sería genial si hubiera una manera de hacer esto solo en bash, sin herramientas de terceros como timeout o netcat.
Graham
3
Sólo quería mencionar que timeoutparece ser parte de GNU coreutils, y se pueden instalar en los Macs con homebrew: brew install coreutils. Luego estará disponible como gtimeout.
onlynone
1
@Wildcard Se sugiere un registro de cambios de bash bash-2.04-devel, aunque es posible que se haya agregado compatibilidad con los nombres de host bash-2.05-alpha1.
Acumenus
109

TOC:

  • Usando bash y timeout
    • Mando
    • Ejemplos
  • Utilizando nc
    • Mando
    • RHEL 6 (nc-1.84)
      • Instalación
      • Ejemplos
    • RHEL 7 (nmap-ncat-6.40)
      • Instalación
      • Ejemplos
  • Observaciones

Usando bash y timeout :

Tenga en cuenta que timeoutdebería estar presente con RHEL 6+, o alternativamente se encuentra en GNU coreutils 8.22. En MacOS, instálelo usandobrew install coreutils y úselo como gtimeout.

Mando:

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

Si parametriza el host y el puerto, asegúrese de especificarlos como ${HOST}y ${PORT}como se muestra arriba. No los especifique simplemente como$HOST y $PORT, es decir, sin las llaves; No funcionará en este caso.

Ejemplo:

Éxito:

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

Fracaso:

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

Si debe preservar el estado de salida de bash,

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

Utilizando nc:

Tenga en cuenta que ncse instala una versión incompatible con versiones anteriores de RHEL 7.

Mando:

Tenga en cuenta que el siguiente comando es único, ya que es idéntico tanto para RHEL 6 como para 7. La instalación y la salida son diferentes.

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

RHEL 6 (nc-1.84):

Instalación:

$ sudo yum install nc

Ejemplos:

Éxito:
$ nc -w 2 -v canyouseeme.org 80 </dev/null; echo $?
Connection to canyouseeme.org 80 port [tcp/http] succeeded!
0
Fracaso:
$ 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

Si el nombre de host se asigna a varias direcciones IP, el comando de falla anterior pasará por muchas o todas ellas. Por ejemplo:

$ 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):

Instalación:

$ sudo yum install nmap-ncat

Ejemplos:

Éxito:
$ 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
Fracaso:
$ nc -w 2 -v canyouseeme.org 81 </dev/null; echo $?
Ncat: Version 6.40 ( http://nmap.org/ncat )
Ncat: Connection timed out.
1

Si el nombre de host se asigna a varias direcciones IP, el comando de falla anterior pasará por muchas o todas ellas. Por ejemplo:

$ 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

Observaciones:

El argumento -v( --verbose) y el echo $?comando son, por supuesto, solo ilustrativos.

Acumenus
fuente
3
timeout 2 bash -c "</dev/tcp/canyouseeme.org/81"; echo $?es un gran punto!
ipeacocks
agregar 2>&1a estado no eco result_test=$(nc -w 2 $ip_addreess 80 </dev/null 2>&1 ; echo $?)
Sanya Snex
55

Con netcatusted puede verificar si un puerto está abierto así:

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

El valor de retorno de ncserá exitoso si se abrió el puerto TCP, y fallará (típicamente el código de retorno 1) si no se pudo establecer la conexión TCP.

Algunas versiones de ncse bloquearán cuando intente esto, ya que no cierran la mitad de envío de su socket incluso después de recibir el fin del archivo /dev/null. En mi propia computadora portátil Ubuntu (18.04), la netcat-openbsdversión de netcat que he instalado ofrece una solución alternativa: la -Nopción es necesaria para obtener un resultado inmediato:

nc -N my.example.com 80 < /dev/null
Brandon Rhodes
fuente
1
Funciona muy bien para sabores ncque no son compatibles con la -wbandera.
David Dossot
55
gracias - también funciona para versiones de nc sin -zsoporte - funciona en RHEL / CentOS 7, por ejemplo
Andrew
1
Aunque este enfoque es bueno, -wes necesario, si no se puede suspender el comando cuando se usa en un script durante más de diez segundos. He escrito una respuesta que incluye -w.
Acumenus
2
+1 esto es genial porque nc viene estándar con alpine linux y ubuntu ambos. Probablemente otros. no es necesario instalar más cuando estás conectado y no puedes. ¡gracias por esto! Podría agregar,nc host port -w 2 && echo it works
std''OrgnlDave
2
Yo prefiero nc -vz localhost 3306. Da una salida más detallada. Lo probé solo en mac.
EFreak
29

En Bash usando archivos de pseudodispositivo para conexiones TCP / UDP es sencillo. Aquí está el guión:

#!/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

Pruebas:

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

Aquí hay una línea (sintaxis Bash):

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

Tenga en cuenta que algunos servidores pueden estar protegidos por firewall de ataques de inundación SYN, por lo que puede experimentar un tiempo de espera de conexión TCP (~ 75 segundos). Para solucionar el problema del tiempo de espera, intente:

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

Consulte: ¿Cómo disminuir el tiempo de espera de llamadas del sistema TCP connect ()?

kenorb
fuente
1
@ABB Esto está relacionado con la configuración de firewall del servidor que no da ninguna respuesta para evitar ataques de inundación SYN. Correr telnet canyouseeme.org 81también se cuelga. Esto está controlado por los límites de tiempo de espera que probablemente están codificados en bash. Ver: disminuir el tiempo de espera de llamadas del sistema TCP connect () .
kenorb
Para la parametrización, ahora parece ser necesario especificar $SERVERy $PORTcon llaves, al menos como ${SERVER}y ${PORT}.
Acumenus
13

Necesitaba una solución más flexible para trabajar en múltiples repositorios git, así que escribí el siguiente código sh basado en 1 y 2 . Puede usar la dirección de su servidor en lugar de gitlab.com y su puerto en reemplazo de 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
Ahmad Yoosofan
fuente
1
¡Gracias! Me facilita la vida a /etc/rc.local
Shahin Khaled
10

Si está utilizando ksh o bash , ambos admiten la redirección de E / S a / desde un socket utilizando la construcción / dev / tcp / IP / PORT . En este shell Korn ejemplo estoy a redirigir no-op de ( : ) STD-en de un socket:

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

El shell imprime un error si el socket no está abierto:

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

Por lo tanto, puede usar esto como prueba en una condición if :

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

El no-op está en una subshell para que pueda tirar std-err si falla la redirección std-in.

A menudo uso / dev / tcp para verificar la disponibilidad de un recurso a través de 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$ 

Este one-liner abre el descriptor de archivo 9 para leer y escribir en el socket, imprime HTTP GET en el socket y utiliza catpara leer desde el socket.

merebagatelle
fuente
1
@ABB Creo que quiere decir que se cuelga durante mucho tiempo si el puerto no responde, como por ejemplo cuando se filtra, o no hay nada en la IP. Un puerto cerrado no causa un retraso si el puerto rechaza activamente la conexión. Para muchos propósitos esto es bastante aceptable.
mc0e
Esta técnica ya fue publicada antes de esta respuesta en una respuesta previa de kenorb . De todos modos, el punto más importante es que, sin embargo, se cuelga durante mucho tiempo si el puerto no responde. Por ejemplo, intente </dev/tcp/canyouseeme.org/80y luego </dev/tcp/canyouseeme.org/81.
Acumenus
9

Si bien era una pregunta anterior, acabo de tratar una variante de la misma, pero ninguna de las soluciones aquí era aplicable, así que encontré otra y la agregué para la posteridad. Sí, sé que el OP dijo que estaban al tanto de esta opción y que no era adecuada para ellos, pero para cualquiera que la siguiera podría resultar útil.

En mi caso, quiero probar la disponibilidad de un apt-cacher-ngservicio local de una dockercompilación. Eso significa que no se puede instalar absolutamente nada antes de la prueba. No se nc, nmap, expect, telneto python. perlSin embargo, está presente, junto con las bibliotecas principales, así que usé esto:

perl -MIO::Socket::INET -e 'exit(! defined( IO::Socket::INET->new("172.17.42.1:3142")))'
mc0e
fuente
6

En algunos casos donde las herramientas como curl, telnet, nc o nmap no están disponibles, aún tiene una oportunidad con 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
fuente
6

verificar puertos usando bash

Ejemplo

$ ./test_port_bash.sh 192.168.7.7 22

el puerto 22 está abierto

Código

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
fuente
4

Si desea usar ncpero no tiene una versión que admita -z, intente usar --send-only:

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

y con tiempo de espera:

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

y sin búsqueda de DNS si es una IP:

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

Devuelve los códigos según -zsi se puede conectar o no.

Chris Mendez
fuente
1

Supongo que es demasiado tarde para una respuesta, y esta podría no ser una buena, pero aquí tienes ...

¿Qué hay de ponerlo dentro de un bucle while con un temporizador de algún tipo? Soy más un tipo Perl que Solaris, pero dependiendo del shell que estés usando, deberías poder hacer algo como:

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

Y luego solo agregue una bandera en el ciclo while, de modo que si se agota el tiempo de espera antes de completar, puede citar el tiempo de espera como motivo de falla.

Sospecho que el telnet también tiene un interruptor de tiempo de espera, pero justo fuera de mi cabeza, creo que lo anterior funcionará.

Thomas rojo
fuente
1

Necesitaba un script corto que se ejecutara en cron y no haya salido Resuelvo mis problemas usando 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

Para ejecutarlo, debe instalar nmap porque no es el paquete instalado por defecto.

Krystian Kulasza
fuente
nmapLa salida de grepable también puede ser útil -oG -para enviarla a stdout. (El grep necesitaría un poco de modificación)
Gert van den Berg
0

Esto usa telnet detrás de escena y parece funcionar bien en mac / linux. No usa netcat debido a las diferencias entre las versiones en linux / mac, y esto funciona con una instalación predeterminada de mac.

Ejemplo:

$ 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 
Brad Parks
fuente
0

Sobre la base de la respuesta más votada, esta es una función para esperar a que se abran dos puertos, con un tiempo de espera también. Tenga en cuenta los dos puertos que deben estar abiertos, 8890 y 1111, así como los max_attempts (1 por segundo).

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
}
João Rocha da Silva
fuente
-1

nmap-ncat para probar el puerto local que aún no está en uso


availabletobindon() {
  port="$1"
  nc -w 2 -i 1 localhost "$port" 2>&1 | grep -v -q 'Idle timeout expired'
  return "$?"
}
isleofgray
fuente
No me dice que mi puerto 80 está abierto.
totoro