207

From a bash script how can I quickly find out whether a port 445 is open/listening on a server.

I have tried a couple of options, but I want something quick:
1. lsof -i :445 (Takes seconds)
2. netstat -an |grep 445 |grep LISTEN (Takes seconds)
3. telnet (it doesn't return)
4. nmap, netcat are not available on the server

It will be nice to know of a way that doesn't enumerate first and greps after that.

jww
  • 97,681
  • 90
  • 411
  • 885
Aman Jain
  • 10,927
  • 15
  • 50
  • 63
  • 1
    Is netcat available? It has a fast fail path IIRC. http://netcat.sourceforge.net/ – JimR Mar 07 '12 at 21:19
  • 5
    `netstat -lnt` (with `-t` and without `-a`) will limit output to listening TCP connections only. It may speed-up a little bit. You can add `-4` for IPv4 only if you don't need IPv6. – Bartosz Moczulski Mar 07 '12 at 21:20
  • lsof -i is a personal favorite. – Matt Joyce Mar 08 '12 at 05:40
  • 15
    `netstat -an | grep PORTNUMBER | grep -i listen` If the output is empty, the port is not in use. – automatix Sep 28 '13 at 13:08
  • I don't know why `lsof` is slow for you, but normally it is the best of the solutions you listed. Your `netstat` solution is not very reliable (you can guess it whenever you use `grep`; anyway it returns true if someone is listening on e.g. 4450). `telnet` and `netcat` actually attempt to create a connection, which may not always be what you want. – petersohn Aug 10 '15 at 07:29
  • try online tools to port details in quickest way - http://www.yougetsignal.com/tools/open-ports/ – Rohit Suthar Mar 11 '16 at 06:55
  • `lsof -n -P -i :445` for improved performance. **`-n`**: _"inhibits the conversion of network numbers to host names"_ **`-P`**: _"inhibits the conversion of port numbers to port names"_ – Dem Pilafian May 04 '20 at 09:22

14 Answers14

169

A surprise I found out recently is that Bash natively supports tcp connections as file descriptors. To use:

exec 6<>/dev/tcp/ip.addr.of.server/445
echo -e "GET / HTTP/1.0\n" >&6
cat <&6

I'm using 6 as the file descriptor because 0,1,2 are stdin, stdout, and stderr. 5 is sometimes used by Bash for child processes, so 3,4,6,7,8, and 9 should be safe.

As per the comment below, to test for listening on a local server in a script:

exec 6<>/dev/tcp/127.0.0.1/445 || echo "No one is listening!"
exec 6>&- # close output connection
exec 6<&- # close input connection

To determine if someone is listening, attempt to connect by loopback. If it fails, then the port is closed or we aren't allowed access. Afterwards, close the connection.

Modify this for your use case, such as sending an email, exiting the script on failure, or starting the required service.

Spencer Rathbun
  • 14,510
  • 6
  • 54
  • 73
  • @AmanJain cat waits for EOF or Ctrl-C to quit. You'll need to adjust this for your protocol. BTW are you running this to a remote server? – Spencer Rathbun Mar 07 '12 at 21:57
  • I want to embed the port checking code in a script on the server, under /etc/init.d/ – Aman Jain Mar 07 '12 at 22:14
  • @AmanJain I've updated it for a local system. You do just want to check if it's listening correct? There isn't any protocol checking, such as requesting a page via http? – Spencer Rathbun Mar 08 '12 at 13:28
  • if a port is taken, it returns nothing, is there any way to make it say "port is taken by " or smth – niken Aug 16 '13 at 17:24
  • @Nik If you add `&&` to the first line, then it can print out that the port is *active*, but by it's nature the command is unaware of *who* has the port. – Spencer Rathbun Aug 22 '13 at 19:08
  • @Spencer i guess u can run lsof -i for that or smth...thnx! – niken Aug 22 '13 at 23:45
  • Added [a simple oneliner](http://stackoverflow.com/a/43364744/1338062) for bash based on this. – user1338062 Apr 12 '17 at 08:53
  • This has a flaw: It takes very long time before throwing error, when the destination packets are getting dropped. For example, if your IP resides in another subnet and gw or firewall drops outgoing packets. – Payam Feb 22 '18 at 00:00
  • Be aware that port 12 is used for the current script, & so is also not safe. – JustinCB Jun 12 '18 at 15:50
  • 2
    This is not a reliable method since not all OS (e.g. ubuntu 16 as I discovered today) are shipped with bash compiled for building the `/dev/tcp/IP/PORT` tree – dyasny Apr 18 '19 at 21:06
  • @dyasny I've just tested both ubuntu 16.04 and .10 docker images and it works fine with bash; I believe you were trying on dash, which unfortunately has been breaking many scripts due to different features from bash – Alberto Chiusole Mar 18 '20 at 14:24
  • Closing with both `>&-` and `<&-` is unnecessary, since both mean the same thing (`close()`). Bash never calls `shutdown()`. – alecov Sep 23 '20 at 22:33
  • there is no `/dev/tcp` device in Debian Bookworm – Dreaded Singleton Dec 19 '22 at 00:34
132

There's a very short with "fast answer" here : How to test if remote TCP port is opened from Shell script?

nc -z <host> <port>; echo $?

I use it with 127.0.0.1 as "remote" address.

this returns "0" if the port is open and "1" if the port is closed

e.g.

nc -z 127.0.0.1 80; echo $?

-z Specifies that nc should just scan for listening daemons, without sending any data to them. It is an error to use this option in conjunc- tion with the -l option.

Black
  • 18,150
  • 39
  • 158
  • 271
  • 2
    That seems to be the easiest way, thanks. The sample script link is not working anymore though, yet it's quite self-explaining anyways. – derFunk Jan 14 '15 at 13:24
  • Nice! This is much faster than the other answers on a server with many ports open. Returns in <0.01 seconds for me while netstat / lsof take 1s+ – Tim Jul 01 '15 at 09:14
  • 2
    The -z flag is not available in the nmap based ncat which most recent distros ship with: Fedora, Centos, etc. (nmap-ncat-6.01-9.fc18.x86_64) – Zack Dec 20 '15 at 14:13
  • 10
    Counter-intuitively, this returns "0" if the port is open and "1" if the port is closed. – Sean Sep 19 '16 at 00:51
  • 3
    @Sean unix commands typically return '0' to indicate success and non-zero for failure. So '0' indicates that it successfully connected and non-zero that it didn't connect for some reason. Note, however, that some versions of 'nc' don't support the '-z' argument so http://stackoverflow.com/a/25793128/6773916 is arguably a better solution. – Rich Sedman Apr 24 '17 at 11:12
  • Is this showing wheter the entry or exit port is open? – Black Mar 03 '20 at 14:49
  • I figured out that this will always return 1 if there is no application running on this port. so this answer is incorrect. It only shows if an application is running on this port right now, in other words, if the port is being used! – Black Mar 03 '20 at 15:37
  • Question- For example if you try `nc -z -v -w 1 imap.mail.yahoo.com 143` (-v verbose), the port is closed/unavailable. But, `nc` still retries 4x - timing out on each retry based on `-w` value (1 second in example). How do you stop it from trying 4x? https://pastebin.com/iQKm3Am6 – B. Shea Nov 03 '20 at 16:18
89

You can use netstat this way for much faster results:

On Linux:

netstat -lnt | awk '$6 == "LISTEN" && $4 ~ /\.445$/'

On Mac:

netstat -anp tcp | awk '$6 == "LISTEN" && $4 ~ /\.445$/'

This will output a list of processes listening on the port (445 in this example) or it will output nothing if the port is free.

anubhava
  • 761,203
  • 64
  • 569
  • 643
  • 1
    your netstat syntax is incorrect. netstat -ln --tcp works, but still slow – Aman Jain Mar 07 '12 at 21:43
  • 6
    Actually it is correct syntax but probably you're using Linux and I am on Mac. For Linux use this: `netstat -lnt | awk '$6 == "LISTEN" && $4 ~ ".445"'` – anubhava Mar 07 '12 at 21:45
  • Windows. I tried `32` with command above, `php -S localhost:32` – Jürgen Paul Feb 22 '13 at 08:34
  • Can't say anything about Windows. This answer was meant for Unix, Linux, Mac etc. On Windows you can install cygwin to get same behavior. – anubhava Feb 22 '13 at 10:16
  • 1
    The question was about linux though, so perhaps the comment should be in the answer. – UpTheCreek Sep 23 '13 at 10:04
  • 1
    In order to check for port 80 I needed to use `awk '$6 == "LISTEN" && $4 ~ "80$"'`. Instead of checking for the dot before the port number with `\.80`, I used `80$`. Otherwise, this also matched IP addresses containing `.80` and ports starting with `80` such as `8000`. – Patrick Oscity Jul 02 '14 at 22:41
  • 1
    @PatrickOscity: Good point, but to make this robust to you need to _combine_ both approaches: `awk '$6 == "LISTEN" && $4 ~ "\.80$"'` – mklement0 Mar 26 '15 at 02:53
  • @mklement0 why are you checking for an explicit dot (.) before the port (when netstat outputs a color before the port)? – Patrick Aug 22 '16 at 19:18
  • @Patrick: I assume you meant _colon_ (`:`). The Mac `netstat` implementation actually uses a `.` (dot) to append the port number (curiously), but on Linux you do need `:`, as you state. – mklement0 Aug 22 '16 at 19:36
40

You can use netcat for this.

nc ip port < /dev/null

connects to the server and directly closes the connection again. If netcat is not able to connect, it returns a non-zero exit code. The exit code is stored in the variable $?. As an example,

nc ip port < /dev/null; echo $?

will return 0 if and only if netcat could successfully connect to the port.

Lars Noschinski
  • 3,667
  • 16
  • 29
Tony
  • 18,776
  • 31
  • 129
  • 193
28

Based on Spencer Rathbun's answer, using bash:

true &>/dev/null </dev/tcp/127.0.0.1/$PORT && echo open || echo closed
user1338062
  • 11,939
  • 3
  • 73
  • 67
  • Good, it will suppress "Connection Refused" message. Auto-exits if the service accept the connection without wait forever. – Seff Feb 26 '19 at 03:46
  • 3
    Best solution for services that send no data after a new connection. About 20 times faster than calling netcat. Can be shortened to `: &>/dev/null – ens Nov 22 '19 at 10:17
  • I'd add a timeout to get a `closed` result faster: `timeout 5s bash -c ': &>/dev/null – emi May 05 '22 at 08:25
  • @emi what platform do you need timeout on? This returns immediately for me if the port is closed. – user1338062 May 06 '22 at 07:01
  • @user1338062 This happens on an Ubuntu Bionic on Azure against external IP (not `localhost` or `127.0.0.1`). – emi May 06 '22 at 08:00
  • @emi ah, I didn't even think of someone using this to test remote ports. Internally bash calls `connect`, so if that blocks (eg. firewall), then you indeed do need a timeout. – user1338062 May 07 '22 at 10:01
  • A timeout would definitely help with the result – graypwn Aug 22 '23 at 06:18
17

they're listed in /proc/net/tcp.

it's the second column, after the ":", in hex:

> cat /proc/net/tcp
  sl  local_address rem_address   st tx_queue rx_queue tr tm->when retrnsmt   uid  timeout inode                                                     
   0: 00000000:0016 00000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 10863 1 ffff88020c785400 99 0 0 10 -1                     
   1: 0100007F:0277 00000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 7983 1 ffff88020eb7b3c0 99 0 0 10 -1                      
   2: 0500010A:948F 0900010A:2328 01 00000000:00000000 02:00000576 00000000  1000        0 10562454 2 ffff88010040f7c0 22 3 30 5 3                   
   3: 0500010A:E077 5F2F7D4A:0050 01 00000000:00000000 02:00000176 00000000  1000        0 10701021 2 ffff880100474080 41 3 22 10 -1                 
   4: 0500010A:8773 16EC97D1:0050 01 00000000:00000000 02:00000BDC 00000000  1000        0 10700849 2 ffff880104335440 57 3 18 10 -1                 
   5: 0500010A:8772 16EC97D1:0050 01 00000000:00000000 02:00000BF5 00000000  1000        0 10698952 2 ffff88010040e440 46 3 0 10 -1                  
   6: 0500010A:DD2C 0900010A:0016 01 00000000:00000000 02:0006E764 00000000  1000        0 9562907 2 ffff880104334740 22 3 30 5 4                    
   7: 0500010A:AAA4 6A717D4A:0050 08 00000000:00000001 02:00000929 00000000  1000        0 10696677 2 ffff880106cc77c0 45 3 0 10 -1  

so i guess one of those :50 in the third column must be stackoverflow :o)

look in man 5 proc for more details. and picking that apart with sed etc is left as an exercise for the gentle reader...

andrew cooke
  • 45,717
  • 10
  • 93
  • 143
10
ss -tl4 '( sport = :22 )'

2ms is quick enough ?

Add the colon and this works on Linux

Community
  • 1
  • 1
leucos
  • 17,661
  • 1
  • 44
  • 34
  • 2
    Great one, `ss` is even slighlty faster than `nc`. `l` is for _listening_, `4` is for IPv4; `sport` stands for (of course) _source port_. The above command is assuming a listening TCP port (`t` option): use `u` option for UDPs, or none of them for both protocols. More info on `ss` as always on [Nixcraft](http://www.cyberciti.biz/tips/linux-investigate-sockets-network-connections.html). NOTE: `ss` filters aren't working here, don't know why (bash 4.3.11, ss utility, iproute2-ss131122), have to go with _grep_. – Campa Apr 13 '15 at 07:30
  • Too bad that `ss` command doesn't return an exit code reflecting its finding; it always returns 0 exit code. – John Greene Jan 16 '18 at 16:14
  • `| grep LISTEN` ? – leucos Jan 17 '18 at 16:45
  • I get `State Recv-Q Send-Q Local Address:Port Peer Address:Port` and now? What does this mean? – Black Mar 03 '20 at 14:44
7
nc -l 8000

Where 8000 is the port number. If the port is free, it will start a server that you can close easily. If it isn't it will throw an error:

nc: Address already in use
Jose Enrique
  • 361
  • 1
  • 3
  • 8
6

Here's one that works for both Mac and Linux:

netstat -aln | awk '$6 == "LISTEN" && $4 ~ "[\\.\:]445$"'
Artur Bodera
  • 1,662
  • 22
  • 20
5

I wanted to check if a port is open on one of our linux test servers. I was able to do that by trying to connect with telnet from my dev machine to the test server. On you dev machine try to run:

$ telnet test2.host.com 8080
Trying 05.066.137.184...
Connected to test2.host.com

In this example I want to check if port 8080 is open on host test2.host.com

razvang
  • 1,055
  • 11
  • 15
2

You can use netcat command as well

[location of netcat]/netcat -zv [ip] [port]

or

nc -zv [ip] [port]

-z – sets nc to simply scan for listening daemons, without actually sending any data to them.
-v – enables verbose mode.

Saurabh
  • 7,525
  • 4
  • 45
  • 46
1

tcping is a great tool with a very low overhead.It also has a timeout argument to make it quicker:

[root@centos_f831dfb3 ~]# tcping 10.86.151.175 22 -t 1
10.86.151.175 port 22 open.
[root@centos_f831dfb3 ~]# tcping 10.86.150.194 22 -t 1
10.86.150.194 port 22 user timeout.
[root@centos_f831dfb3 ~]# tcping 1.1.1.1 22 -t 1
1.1.1.1 port 22 closed.
Payam
  • 1,068
  • 12
  • 15
  • 1
    Not sure tcping is worth installing when Spencer's solution requires no extra installs, but this is def the cleanest and most human-readable solution. – bdombro May 16 '18 at 21:24
-1

nmap is the right tool. Simply use nmap example.com -p 80

You can use it from local or remote server. It also helps you identify if a firewall is blocking the access.

zainengineer
  • 13,289
  • 6
  • 38
  • 28
-4

If you're using iptables try:

iptables -nL

or

iptables -nL | grep 445
Noz
  • 6,216
  • 3
  • 47
  • 82