web-dev-qa-db-fra.com

Teste si le port distant TCP est ouvert à partir d'un script Shell

Je recherche une méthode simple et rapide pour tester correctement si un port donné TCP est ouvert sur un serveur distant, à partir d'un script Shell.

J'ai réussi à le faire avec la commande telnet, et cela fonctionne bien lorsque le port est ouvert, mais il ne semble pas expirer quand il ne l'est pas et se bloque juste là ...

Voici un exemple:

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

J'ai besoin d'un meilleur moyen ou d'un moyen de forcer le délai d'expiration de telnet s'il ne se connecte pas en moins de 8 secondes, par exemple, et de renvoyer quelque chose que je peux attraper dans Shell (code de retour ou chaîne in stdout).

Je connais la méthode Perl, qui utilise le module IO :: Socket :: INET et a écrit un script réussi qui teste un port, mais préfère plutôt éviter d'utiliser Perl si possible.

Remarque: Voici ce que mon serveur est en cours d'exécution (où je dois l'exécuter à partir de)

SunOS 5.10 Generic_139556-08 i86pc i386 i86pc

279
Yanick Girouard

Comme l'a souligné B. Rhodes, nc fera le travail. Une façon plus compacte de l'utiliser:

nc -z <Host> <port>

De cette façon, nc ne vérifiera que si le port est ouvert, quitte avec 0 en cas de succès, 1 en cas d'échec.

Pour un contrôle interactif rapide (avec un délai d'attente de 5 secondes):

nc -z -v -w5 <Host> <port>
426
Alessio Gaeta

C'est assez facile à faire avec les options _-z_ et _-w TIMEOUT_ sur nc, mais tous les systèmes n'ont pas nc installé. Si vous avez une version assez récente de bash, cela fonctionnera:

_# 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
_

Ce qui se passe ici, c'est que timeout exécutera la sous-commande et la tuera si elle ne se ferme pas dans le délai imparti (1 seconde dans l'exemple ci-dessus). Dans ce cas, bash est la sous-commande et utilise sa commande spéciale / dev/tcp pour essayer d'ouvrir une connexion au serveur et au port spécifiés. Si bash peut ouvrir la connexion dans le délai imparti, cat la fermera immédiatement (car il lit _/dev/null_) et se termine avec un code d’état de _0_ qui se propage. à travers bash puis timeout. Si bash obtient un échec de connexion avant le délai spécifié, alors bash sortira avec un code de sortie de 1 que timeout retournera également. Et si bash ne parvient pas à établir une connexion et que le délai spécifié expire, alors timeout va tuer bash et quitter avec un statut de 124.

133
onlynone

COT:

  • Utilisation de bash et timeout
    • Commander
    • Exemples
  • Utilisation de nc
    • Commander
    • RHEL 6 (nc-1.84)
      • Installation
      • Exemples
    • RHEL 7 (nmap-ncat-6.40)
      • Installation
      • Exemples
  • Remarques

Utiliser bash et timeout:

Notez que timeout devrait être présent avec RHEL 6+, ou se trouve également dans GNU coreutils 8.22. Sous MacOS, installez-le avec brew install coreutils et utilisez-le comme gtimeout.

Commander:

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

Si vous paramétrez l'hôte et le port, veillez à les spécifier comme suit: ${Host} et ${PORT} comme ci-dessus. Ne les spécifiez pas simplement comme $Host et $PORT, c'est-à-dire sans les accolades; cela ne fonctionnera pas dans ce cas.

Exemple:

Succès:

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

Échec:

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

Si vous devez conserver le statut de sortie de bash,

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

Utiliser nc:

Notez qu'une version incompatible avec les versions antérieures de nc est installée sur RHEL 7.

Commander:

Notez que la commande ci-dessous est unique en ce sens qu'elle est identique pour RHEL 6 et 7. Seules l'installation et la sortie sont différentes.

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

RHEL 6 (nc-1,84):

Installation:

$ Sudo yum install nc

Exemples:

$ 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

Si le nom d'hôte correspond à plusieurs adresses IP, la commande défaillante ci-dessus parcourt la totalité ou la totalité d'entre elles. Par exemple:

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

Installation:

$ Sudo yum install nmap-ncat

Exemples:

$ 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

Si le nom d'hôte correspond à plusieurs adresses IP, la commande défaillante ci-dessus parcourt la totalité ou la totalité d'entre elles. Par exemple:

$ 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

Remarques:

L'argument -v (--verbose) et la commande echo $? ne sont bien sûr qu'illustrés.

98
Acumenus

Avec netcat, vous pouvez vérifier si un port est ouvert comme ceci:

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

La valeur de retour de nc sera réussie si le port TCP est ouvert et échoue (généralement le code de retour 1) s'il ne peut pas établir la connexion TCP.

Certaines versions de nc seront bloquées lorsque vous tenterez ceci, car elles ne fermeront pas la moitié d'envoi de leur socket, même après avoir reçu la fin du fichier de /dev/null. Sur mon propre ordinateur portable Ubuntu (18.04), la version netcat-openbsd de netcat que j'ai installée offre une solution de contournement: l'option -N est nécessaire pour obtenir un résultat immédiat:

nc -N my.example.com 80 < /dev/null
50
Brandon Rhodes

Dans Bash, l'utilisation de fichiers de pseudo-périphériques pour les connexions TCP/UDP est simple. Voici le script:

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

Essai:

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

Voici one-liner (syntaxe Bash):

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

Notez que certains serveurs peuvent être protégés par un pare-feu contre les attaques par saturation SYN. ​​Vous risquez donc de rencontrer un délai de connexion TCP (environ 75 secondes). Pour contourner le problème de délai d'attente, essayez:

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

Voir: Comment diminuer TCP connect () délai d’appel système?

26
kenorb

J'avais besoin d'une solution plus flexible pour travailler sur plusieurs référentiels git. J'ai donc écrit le code sh suivant basé sur 1 et 2 . Vous pouvez utiliser votre adresse de serveur au lieu de gitlab.com et votre port au lieu 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
11
Ahmad Yoosofan

Si vous utilisez ksh ou bash, ils prennent tous les deux en charge la redirection IO vers/depuis un socket à l'aide de la commande / dev/tcp/IP/PORT construire. Dans cet exemple Korn Shell, je redirige le std-in (:) no-op depuis 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

Le shell imprime une erreur si le socket n’est pas ouvert:

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

Vous pouvez donc l'utiliser comme test dans une condition if:

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

Le no-op est dans un sous-shell afin que je puisse jeter std-err si la redirection std-in échoue.

J'utilise souvent / dev/tcp pour vérifier la disponibilité d'une ressource sur 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$ 

Cette ligne s'ouvre descripteur de fichier 9 pour lire et écrire dans le socket, affiche le HTTP GET dans le socket et utilise cat pour lire de la prise.

10
merebagatelle

Bien qu’il s’agisse d’une vieille question, j’ai abordé une variante de celle-ci, mais aucune des solutions ici n’était applicable, j’en ai donc trouvé une autre et je l’ajoute pour la postérité. Oui, je sais que le PO a dit qu'il était au courant de cette option et que cela ne lui convenait pas, mais cela pourrait s'avérer utile pour les personnes suivantes.

Dans mon cas, je veux tester la disponibilité d'un service apt-cacher-ng local à partir d'une version docker. Cela signifie que rien ne peut être installé avant le test. Non nc, nmap, expect, telnet ou python. Perl cependant est présent, avec les librairies principales, donc j'ai utilisé:

Perl -MIO::Socket::INET -e 'exit(! defined( IO::Socket::INET->new("172.17.42.1:3142")))'
9
mc0e

Dans certains cas où des outils tels que curl, telnet, nc o nmap sont indisponibles, vous avez encore une chance avec 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
6
PRF

vérifier les ports en utilisant bash

Exemple

$ ./test_port_bash.sh 192.168.7.7 22

le port 22 est ouvert

Code

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
5
krbu

Si vous souhaitez utiliser nc mais que vous n'avez pas de version prenant en charge -z, essayez d'utiliser --send-only:

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

et avec timeout:

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

et sans recherche DNS s'il s'agit d'une adresse IP:

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

Il renvoie les codes sous forme de -z selon qu'il peut ou non se connecter.

4
Chris Mendez

J'imagine qu'il est trop tard pour une réponse et que ce n'est peut-être pas une bonne réponse, mais voilà ...

Qu'en est-il de le mettre dans une boucle while avec une minuterie dessus? Je suis plus un gars de Perl que Solaris, mais selon le shell que vous utilisez, vous devriez pouvoir faire quelque chose comme:

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

Ajoutez ensuite un indicateur dans la boucle while afin que, si le délai expire avant la fin, vous pouvez indiquer le délai comme motif d'échec.

Je soupçonne que le telnet a également un commutateur de délai d’attente, mais je pense que ce qui précède va fonctionner.

1
Red Thomas

J'avais besoin d'un court script qui a été exécuté dans cron et qui n'a pas été généré. Je résous mes problèmes en utilisant 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

Pour l'exécuter, vous devez installer nmap car ce n'est pas le paquet installé par défaut.

1
Krystian Kulasza

Cela utilise telnet dans les coulisses et semble fonctionner correctement sous mac/linux. Il n'utilise pas netcat en raison des différences entre les versions de linux/mac, et cela fonctionne avec une installation mac par défaut.

Exemple:

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

Sur la base de la réponse la plus votée, voici une fonction qui permet d’attendre que deux ports soient ouverts, avec également un délai d’attente. Notez les deux ports qui doivent être ouverts, 8890 et 1111, ainsi que le max_attempts (1 par seconde).

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
}
0