Obtain a Random Available TCP Port with Bash
On Linux, we might sometimes want to choose an unused TCP port randomly. This occurs from time to time on a server, when the administrator wants to expose an HTTP port for a user. Or, you just need an available port for IPC. Let’s make it happen with pure bash scripting.
function unused_port() {
N=${1:-1}
comm -23 \
<(seq "1025" "65535" | sort) \
<(ss -Htan |
awk '{print $4}' |
cut -d':' -f2 |
sort -u) |
shuf |
head -n "$N"
}
We would take apart the function step by step in the following paragraphs.
Tirst step is to obtain a list of occupied ports, which can be accomplished by command
ss -Htan
ss
is a tool for investigating sockets, which prints out ports currently used in the form of a table:
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
ESTAB 0 0 127.0.0.1:45342 127.0.0.1:1081
...
Argument -Htan
controls the printing style of ss
. -H
removes table header; -t
lists only TCP ports; -a
lists all ports, including listening and unlistening ones; -n
enforces ports to be printed numerically 1. The command produces an output like:
LISTEN 0 128 0.0.0.0:17500 0.0.0.0:*
LISTEN 0 128 127.0.0.1:17600 0.0.0.0:*
We then use some string manipulation tools to extract the port numbers:
ss -Htan |
# hl: begin
awk '{print $4}' |
cut -d':' -f2 |
sort -u
# hl: end
Here, awk '{print $4}'
selects the 4th item (e.g., 0.0.0.0:17500
) from each line. cut -d':' -f2
splits each item with :
and prints out the second part (e.g., 17500
). sort -u
sorts the items and removes duplicated ones.
Now we’ve got the list of unavailable ports. We might name it as LIST2
for simplicity. The next step is to create another list LIST1
by “inversing” LIST2
. By “inversing” we mean LIST1
(disjointly) unioning LIST2
would be equal to FULLLIST
(all legal ports).
FULLLIST
can be obtained easily with seq "1025" "65535" | sort
. The “inverse” operation, meanwhile, can be achieved using comm
.
Simply,
comm
takes two filesFILE1
andFILE2
as input, and produces output with three columns:
- Column 1 contains lines unique to
FILE1
;- Column 2 contains lines unique to
FILE2
;- Column 3 contains lines common to both files.
Apparently we only needs the first column, so use -23
to suppress the other two ones. Put them together as:
comm -23 \
<(seq "1025" "65535" | sort) \
<(ss -Htan |
awk '{print $4}' |
cut -d':' -f2 |
sort -u)
The syntax <(command)
is known as process substitution. It is equivalent to:
- Spawn
command
in current shell and pipe its stdout to/dev/fd/<some number>
; - Substitute
<(...)
with/dev/fd/<some number>
.
The last step, we further incorporate the command into a convenient function.
# hl: begin
function unused_port() {
N=${1:-1}
# hl: end
comm -23 \
<(seq "1025" "65535" | sort) \
<(ss -Htan |
awk '{print $4}' |
cut -d':' -f2 |
sort -u) |
# hl: begin
shuf |
head -n "$N"
# hl: end
}
The function receives an argument N
, shuffles the available port numbers, and choose N
ports from the list.
Code is available at hsfzxjy/bashi on Github.
References
- Otherwise, some ports would be resolved into their service names, such as
80
intohttp
Author: hsfzxjy.
Link: .
License: CC BY-NC-ND 4.0.
All rights reserved by the author.
Commercial use of this post in any form is NOT permitted.
Non-commercial use of this post should be attributed with this block of text.
OOPS!
A comment box should be right here...But it was gone due to network issues :-(If you want to leave comments, make sure you have access to disqus.com.