If you are writing a startup or monitoring script, you may want a way to check if a TCP port is listening in bash. You can actually do it without calling any external programs, such as ss
or netstat
.
I have only done this on Linux, so if you are using another UNIX like operating system, such as FreeBSD or OSX, this may not work. I would not recommend trying this with UDP without extensive testing, as it is a connectionless protocol and will probably always show that the port is listening. When I did some limited testing with UDP on my Debian Bullseye machine, UDP always worked. That is probably not what you want.
In the REDIRECTION section of the man page, there is a section describing filenames that are treated specially. Take note of this section:
/dev/tcp/host/port If host is a valid hostname or Internet address, and port is an integer port number or service name, bash attempts to open the corresponding TCP socket.
This means you can use bash to read from or write to a network connection using standard redirect operators (E.g. <
, >
). In order to specify the address and port, just replace host/port
with the applicable combination. The host
portion can be any valid network address or resolvable host name. In the examples, I will use various types including IPv4, IPv6, DNS name, and a name defined in /etc/hosts
.
In our case, we just want to check if a TCP port is listening. All you need is the >
redirect operator. For example:
$ > /dev/tcp/localhost/24
bash: connect: Connection refused
bash: /dev/tcp/localhost/24: Connection refused
$ echo $?
1
$ > /dev/tcp/::1/22
$ echo $?
0
Notice how the return code of the command was 1
when it couldn’t connect 0
and when it was successful? You can place the command directly in a conditional statement:
$ if > /dev/tcp/tylersguides.com/1234; then echo success; else echo fail; fi
bash: connect: Connection refused
bash: /dev/tcp/tylersguides.com/1234: Connection refused
fail
$ if > /dev/tcp/tylersguides.com/443 2> /dev/null; then echo success; else echo fail; fi
success
If you wish to suppress the error output on failure, place 2> /dev/null
at the beginning to redirect stderr
to /dev/null
.
$ if 2> /dev/null > /dev/tcp/tylersguides.com/1234
then
echo success
else
echo fail
fi
fail
With processes that take a long time to start, such as Java applications running in an application server, you may wish to monitor the port with a while
loop.
Suppose you are writing a startup script that starts a process that listens on port 7001
and you want it to give up after 5 minutes. You could use the following:
let -i t=0
let -i timeout=300
while (! 2> /dev/null > /dev/tcp/::1/7001 ) || [[ $t -lt $timeout ]]
do
sleep 1
t=$(($t + 1))
done
if [[ $t -ge $timeout ]]
then
echo timeout
exit 1
fi
If you aren’t familiar with bash scripting, let -i
means a variable will be storing an integer instead of a string. The -lt
and -ge
are synonymous with the math operators < and >=, respectively. I.e. they compare numbers. Bash has the comparison operators <
and >
, but they perform lexicographic comparison rather than arithmetic comparison. Bash does not have <=, or >=. You will get a syntax error if you try them.