Kiểm tra xem cổng TCP từ xa có mở từ tập lệnh shell không


297

Tôi đang tìm kiếm một phương pháp nhanh chóng và đơn giản để kiểm tra chính xác nếu một cổng TCP cụ thể được mở trên một máy chủ từ xa, từ bên trong tập lệnh Shell.

Tôi đã quản lý để thực hiện nó bằng lệnh telnet và nó hoạt động tốt khi cổng được mở, nhưng dường như nó không hết thời gian khi nó không và chỉ bị treo ở đó ...

Đây là một mẫu:

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

Tôi cần một cách tốt hơn hoặc cách buộc telnet hết thời gian chờ nếu nó không kết nối dưới 8 giây chẳng hạn và trả lại thứ tôi có thể bắt được trong Shell (mã trả về hoặc chuỗi trong thiết bị xuất chuẩn).

Tôi biết phương pháp Perl, sử dụng mô đun IO :: Socket :: INET và đã viết một kịch bản thành công để kiểm tra một cổng, nhưng muốn tránh sử dụng Perl nếu có thể.

Lưu ý: Đây là những gì máy chủ của tôi đang chạy (nơi tôi cần chạy nó từ)

SunOS 5.10 Chung_139556-08 i86pc i386 i86pc


Những gì về Netcat hoặc Nmap ?
Jeremiah Willcock

Câu trả lời nói dối với Mong đợi. Chúng tôi đã viết một tập lệnh đơn giản gửi một telnet trên cổng mà chúng tôi cần, với thời gian chờ là 8 giây. Có rất nhiều ví dụ để chọn quá. Chúng tôi dựa trên bài đăng này: unix.com/shell-programming-scripting/,
Yanick Girouard

1
check_tcp từ github.com/monitoring-plugins/monitoring-plugins có thể làm điều này, bao gồm nhập chuỗi và kiểm tra câu trả lời dự kiến.

Câu trả lời:


452

Như được chỉ ra bởi B. Rhodes, ncsẽ thực hiện công việc. Một cách nhỏ gọn hơn để sử dụng nó:

nc -z <host> <port>

Cách đó ncsẽ chỉ kiểm tra xem cổng có mở không, thoát với 0 khi thành công, 1 khi thất bại.

Để kiểm tra tương tác nhanh (với thời gian chờ 5 giây):

nc -z -v -w5 <host> <port>

48
centos7 mặc định sử dụng nmap netcat và không có tùy chọn -z.
jolestar

7
Điều này không hoạt động trong RHEL / Centos. Đối với những bản phân phối đó, bạn cần phải: nc -vn <host> <port>
Justin Dennahower

2
FWIW, tôi đã đại tu hoàn toàn câu trả lời của mình bằng một ví dụ , áp dụng riêng cho cả RHEL 6 và RHEL 7.
Acumenus

4
ít nhất trên Mac, bạn có thể cần thêm -G#để đặt thời gian chờ kết nối tách biệt với / ngoài -w#thời gian chờ, về cơ bản hoạt động như thời gian chờ đọc.
Doktor J

1
@jolestar Bạn có thể tự nâng cấp Ncat trên Centos 7 để có -ztùy chọn. Bạn có thể muốn xem xét: unix.stackexchange.com/questions/393762/ triệt
Damien Ó Ceallaigh

150

Nó đủ dễ thực hiện với các tùy chọn -zvà , nhưng không phải tất cả các hệ thống đã được cài đặt. Nếu bạn có một phiên bản bash đủ gần đây, nó sẽ hoạt động:-w TIMEOUTncnc

# 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

Điều đang xảy ra ở đây là timeoutsẽ chạy tiểu ban và tiêu diệt nó nếu nó không thoát trong khoảng thời gian chờ đã chỉ định (1 giây trong ví dụ trên). Trong trường hợp bashnày là tiểu ban và sử dụng xử lý đặc biệt / dev / tcp của nó để thử và mở một kết nối đến máy chủ và cổng được chỉ định. Nếu bashcó thể mở kết nối trong thời gian chờ, sẽ tắt và thoát với trạng thái là 124.cat sẽ chỉ cần đóng nó ngay lập tức (vì nó đọc từ đó /dev/null) và thoát với mã trạng thái 0sẽ truyền qua bashvà sau đó timeout. Nếu bashgặp lỗi kết nối trước thời gian chờ đã chỉ định, thì bashsẽ thoát với mã thoát là 1 timeoutcũng sẽ trả về. Và nếu bash không thể thiết lập kết nối và hết thời gian chờ đã chỉ định, thìtimeoutbash


1
/dev/tcpsẵn trên các hệ thống khác ngoài Linux không? Còn máy Mac nói riêng thì sao?
Peter

8
Điều / dev / tcp là một tính năng của bash, vì vậy, có. Tuy nhiên, có vẻ như máy Mac không có thời gian chờ ...
diễn ra vào

1
@onlynone - Có vẻ như timeoutcũng không tồn tại trong FreeBSD và trên hộp Ubuntu cũ của tôi, đó là một gói có thể cài đặt, nhưng theo mặc định thì không có. Sẽ thật tuyệt nếu có một cách để làm điều này trong bash một mình, mà không có các công cụ của bên thứ ba như thời gian chờ hoặc netcat.
Graham

3
Chỉ muốn đề cập rằng timeoutdường như là một phần của lõi GNU và có thể được cài đặt trên máy Mac với homebrew : brew install coreutils. Sau đó nó sẽ có sẵn như là gtimeout.
chỉ

1
@Wildcard Nhật ký thay đổi bash cho thấy bash-2.04-devel, mặc dù hỗ trợ cho tên máy chủ có thể đã được thêm vào bash-2.05-alpha1.
Acumenus

109

TOC:

  • Sử dụng bash và timeout
    • Chỉ huy
    • Ví dụ
  • Sử dụng nc
    • Chỉ huy
    • MÙA 6 (nc-1.84)
      • Cài đặt
      • Ví dụ
    • RHEL 7 (nmap-ncat-6,40)
      • Cài đặt
      • Ví dụ
  • Nhận xét

Sử dụng bash và timeout:

Lưu ý rằng timeoutphải có mặt với RHEL 6+ hoặc được tìm thấy thay thế trong GNU coreutils 8.22. Trên MacOS, cài đặt nó bằng cách sử dụng brew install coreutilsvà sử dụng nó như gtimeout.

Chỉ huy:

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

Nếu tham số hóa máy chủ và cổng, hãy chắc chắn chỉ định chúng như ${HOST}${PORT}như trên. Không chỉ định chúng chỉ là $HOST$PORT, tức là không có niềng răng; nó sẽ không hoạt động trong trường hợp này.

Thí dụ:

Sự thành công:

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

Sự thất bại:

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

Nếu bạn phải duy trì trạng thái thoát của bash,

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

Sử dụng nc:

Lưu ý rằng phiên bản không tương thích ngược ncđược cài đặt trên RHEL 7.

Chỉ huy:

Lưu ý rằng lệnh bên dưới là duy nhất ở chỗ nó giống hệt nhau cho cả RHEL 6 và 7. Đây chỉ là cài đặt và đầu ra khác nhau.

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

MÙA 6 (nc-1.84):

Cài đặt:

$ sudo yum install nc

Ví dụ:

Sự thành công:
$ nc -w 2 -v canyouseeme.org 80 </dev/null; echo $?
Connection to canyouseeme.org 80 port [tcp/http] succeeded!
0
Sự thất bại:
$ 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

Nếu tên máy chủ ánh xạ tới nhiều IP, lệnh không thành công ở trên sẽ chuyển qua nhiều hoặc tất cả chúng. Ví dụ:

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

Cài đặt:

$ sudo yum install nmap-ncat

Ví dụ:

Sự thành công:
$ 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
Sự thất bại:
$ nc -w 2 -v canyouseeme.org 81 </dev/null; echo $?
Ncat: Version 6.40 ( http://nmap.org/ncat )
Ncat: Connection timed out.
1

Nếu tên máy chủ ánh xạ tới nhiều IP, lệnh không thành công ở trên sẽ chuyển qua nhiều hoặc tất cả chúng. Ví dụ:

$ 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

Nhận xét:

Đối số -v( --verbose) và echo $?lệnh tất nhiên chỉ để minh họa.


3
timeout 2 bash -c "</dev/tcp/canyouseeme.org/81"; echo $?là điểm tuyệt vời!
ipeacocks

thêm 2>&1vào trạng thái không lặp lại result_test=$(nc -w 2 $ip_addreess 80 </dev/null 2>&1 ; echo $?)
Sanya Snex

55

Với netcatbạn có thể kiểm tra xem một cổng có mở như thế này không:

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

Giá trị trả về ncsẽ thành công nếu cổng TCP được mở và thất bại (thường là mã trả về 1) nếu nó không thể thực hiện kết nối TCP.

Một số phiên bản ncsẽ bị treo khi bạn thử điều này, vì chúng không đóng một nửa ổ cắm của chúng ngay cả sau khi nhận được phần cuối của tệp /dev/null. Trên máy tính xách tay Ubuntu của riêng tôi (18.04), netcat-openbsdphiên bản netcat mà tôi đã cài đặt cung cấp một cách giải quyết: -Ntùy chọn là cần thiết để có kết quả ngay lập tức:

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

1
Hoạt động tuyệt vời cho hương vị ncmà không hỗ trợ -wcờ.
David Dossot

5
cảm ơn - cũng hoạt động cho các phiên bản của nc mà không cần -zhỗ trợ - hoạt động trên RHEL / CentOS 7, chẳng hạn
Andrew

1
Mặc dù cách tiếp cận này là tốt, nhưng -wlà cần thiết, nhưng thất bại mà lệnh khi được sử dụng trong tập lệnh có thể bị treo trong hơn mười giây. Tôi đã viết một câu trả lời bao gồm -w.
Acumenus

2
+1 điều này thật tuyệt vời vì nc có tiêu chuẩn với cả alpine linux và ubfox. có lẽ là những người khác không cần cài đặt thêm khi bạn từ xa và không thể. cảm ơn vì điều đó! Tôi có thể thêm,nc host port -w 2 && echo it works
std''OrgnlDave

2
Tôi thích nc -vz localhost 3306. Nó cho một đầu ra dài dòng hơn. Đã thử nó trên mac.
EFreak

29

Trong Bash, việc sử dụng các tệp giả thiết bị cho các kết nối TCP / UDP rất đơn giản. Đây là kịch bả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

Kiểm tra:

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

Đây là một-liner (cú pháp Bash):

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

Lưu ý rằng một số máy chủ có thể được bảo vệ tường lửa khỏi các cuộc tấn công lũ lụt SYN, do đó bạn có thể gặp thời gian chờ kết nối TCP (~ 75 giây). Để khắc phục sự cố hết thời gian, hãy thử:

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

Xem: Làm thế nào để giảm thời gian chờ cuộc gọi hệ thống TCP ()?


1
@ABB Điều này liên quan đến cấu hình tường lửa của máy chủ không cung cấp bất kỳ phản hồi nào để ngăn chặn mọi cuộc tấn công lũ lụt SYN. Chạy telnet canyouseeme.org 81cũng bị treo. Điều này được kiểm soát bởi các giới hạn thời gian chờ của bạn mà có lẽ được mã hóa cứng trong bash. Xem: giảm thời gian gọi hệ thống TCP connect () .
kenorb

Đối với tham số hóa, bây giờ dường như là cần thiết để chỉ định $SERVER$PORTvới niềng răng, ít nhất là ${SERVER}${PORT}.
Acumenus

13

Tôi cần một giải pháp linh hoạt hơn để làm việc trên nhiều kho git vì vậy tôi đã viết mã sh sau dựa trên 12 . Bạn có thể sử dụng địa chỉ máy chủ của mình thay vì gitlab.com và cổng của bạn thay thế 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
Cảm ơn! Nó làm cho cuộc sống của tôi dễ dàng cho /etc/rc.local
Shahin Khaled

10

Nếu bạn đang sử dụng ksh hoặc bash , cả hai đều hỗ trợ chuyển hướng IO đến / từ một ổ cắm bằng cách sử dụng cấu trúc / dev / tcp / IP / PORT . Trong này Korn shell dụ tôi đang chuyển hướng không-op ( : ) std-in từ một ổ cắm:

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

Shell in lỗi nếu ổ cắm không mở:

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

Do đó, bạn có thể sử dụng điều này làm thử nghiệm trong điều kiện 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 nằm trong một subshell để tôi có thể ném std-err đi nếu chuyển hướng std-in thất bại.

Tôi thường sử dụng / dev / tcp để kiểm tra tính khả dụng của tài nguyên qua 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$ 

Lớp lót này mở mô tả tệp 9 để đọc và ghi vào ổ cắm, in HTTP GET sang ổ cắm và sử dụng catđể đọc từ ổ cắm.


1
@ABB Tôi nghĩ bạn có nghĩa là nó bị treo trong một thời gian dài nếu cổng không phản hồi, ví dụ như khi được lọc, hoặc không có gì trên IP. Cổng đóng không gây chậm trễ nếu cổng chủ động từ chối kết nối. Đối với nhiều mục đích, điều này là hoàn toàn chấp nhận được.
mc0e

Kỹ thuật này đã được đăng trước câu trả lời này trong câu trả lời trước của kenorb . Dù sao, điểm lớn hơn là nó bị treo trong một thời gian dài nếu cổng không phản hồi. Ví dụ, thử </dev/tcp/canyouseeme.org/80và sau đó </dev/tcp/canyouseeme.org/81.
Acumenus

9

Mặc dù là một câu hỏi cũ, tôi chỉ giải quyết một biến thể của nó, nhưng không có giải pháp nào ở đây có thể áp dụng được, vì vậy tôi đã tìm thấy một giải pháp khác và đang bổ sung nó cho hậu thế. Vâng, tôi biết OP nói rằng họ biết về tùy chọn này và nó không phù hợp với họ, nhưng với bất kỳ ai theo sau nó có thể hữu ích.

Trong trường hợp của tôi, tôi muốn kiểm tra tính khả dụng của apt-cacher-ngdịch vụ địa phương từ bản dockerdựng. Điều đó có nghĩa là hoàn toàn không có gì có thể được cài đặt trước khi thử nghiệm. Không nc, nmap, expect, telnethoặc python. perltuy nhiên có mặt, cùng với các thư viện cốt lõi, vì vậy tôi đã sử dụng điều này:

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

6

Trong một số trường hợp các công cụ như curl, telnet, nc o nmap không khả dụng, bạn vẫn có cơ hội với 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

kiểm tra cổng bằng bash

Thí dụ

$ ./test_port_bash.sh 192.168.7.7 22

cổng 22 đang mở

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

4

Nếu bạn muốn sử dụng ncnhưng không có phiên bản hỗ trợ -z, hãy thử sử dụng --send-only:

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

và với thời gian chờ:

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

và không có tra cứu DNS nếu đó là IP:

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

Nó trả về các mã như -zdựa trên việc nó có thể kết nối hay không.


1

Tôi đoán rằng đã quá muộn để trả lời, và đây có thể không phải là một câu trả lời hay, nhưng ở đây bạn đi ...

Điều gì về việc đặt nó bên trong một vòng lặp while với một bộ đếm thời gian trên đó. Tôi là một chàng trai Perl hơn Solaris, nhưng tùy thuộc vào lớp vỏ bạn đang sử dụng, bạn sẽ có thể làm một cái gì đó như:

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

Và sau đó chỉ cần thêm một cờ trong vòng lặp while, để nếu nó hết thời gian trước khi hoàn thành, bạn có thể trích dẫn thời gian chờ là lý do cho sự thất bại.

Tôi nghi ngờ rằng telnet cũng có một công tắc hết thời gian, nhưng chỉ cần ra khỏi đầu, tôi nghĩ rằng những điều trên sẽ hoạt động.


1

Tôi cần đoạn script ngắn được chạy bằng cron và không xuất ra. Tôi giải quyết vấn đề của mình bằng cách sử dụng 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

Để chạy nó Bạn nên cài đặt nmap vì nó không phải là gói cài đặt mặc định.


nmapĐầu ra có thể chỉnh sửa của bạn cũng có thể hữu ích -oG -để gửi nó đến thiết bị xuất chuẩn. (grep sẽ cần một chút sửa đổi)
Gert van den Berg

0

Điều này sử dụng telnet đằng sau hậu trường và dường như hoạt động tốt trên mac / linux. Nó không sử dụng netcat vì sự khác biệt giữa các phiên bản trên linux / mac và điều này hoạt động với cài đặt mac mặc định.

Thí dụ:

$ 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

Dựa trên câu trả lời được bình chọn nhiều nhất, đây là chức năng chờ hai cổng được mở, cùng với thời gian chờ. Lưu ý hai cổng được mở, 8890 và 1111, cũng như max_attvor (1 mỗi giây).

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 để kiểm tra cổng cục bộ chưa được sử dụng


availabletobindon() {
  port="$1"
  nc -w 2 -i 1 localhost "$port" 2>&1 | grep -v -q 'Idle timeout expired'
  return "$?"
}

Nó không cho tôi biết rằng cổng 80 của tôi đang mở.
totoro
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.