I'm trying to write a script that takes a port number as an argument. It returns the next port that is not assigned to anything, and checks it by using the file /etc/services. If the port is taken (i.e. listed in the document), it adds one and then tries again.  I can't seem to get this script to return anything for found - it always equals 0 so I never enter the while loop.  What am I doing wrong?#!/bin/bashport=$1found=$(cat /etc/services | grep -o '[[:space:]]$port/' | wc -l)while [ $found -ge 1 ]; do    $port=$($port+1)#doneecho found: $foundecho port: $port(ignore the comment in front of done, that's not the problem)
What am I doing wrong trying to write a bash script that returns the number of the next available port?
bash;shell script;shell;networking;grep
OverviewThere are just a few minor errors with this script, and a few stylistic things I would change.  Let's go through the original line-by-line:#!/bin/bashport=$1A typical #! line and a simple assignment, we're off to a good start.found=$(cat /etc/services | grep -o '[[:space:]]$port/' | wc -l)This line has a small issue that one may not notice at first glance.  Since $port is in strong quotes, '$port', it won't expand to its value.  You'll want to use weak quotes instead, $port.  Also, you don't really need cat here.  grep takes a filename as an argument, but if it didn't you could always use redirection (grep 'pattern' < /etc/services).  Further, grep -c writes a count of matching lines, so wc -l is redundant here as well.while [ $found -ge 1 ]; do    $port=$($port+1)doneThe loop seems almost fine.  You'll want to quote the entities in the test in case $found could possibly be empty.  Also, you assign with port=, not $port= (but you've used the correct form above, so I assume this was just a typo).  Finally $($port+1) means (if port is 22, for example) The output of the command 22+1.  Clearly you want The result of the arithmetic expression 22+1, which is the very similar $(($port+1)).echo found: $foundecho port: $portThese lines are alright as is.Considering the logicNow, if you make all of the above changes so that the program is syntactically correct, it still will not do what you want.  You don't list a correct output, so I'm assuming you want:found: <the number of occurrences of $1 in /etc/services>port: <the next free port>If these assumptions are incorrect, let me know in the comments and I will edit accordingly.The script as is will never return if the given port does appear in /etc/services because you never update found.  So you may think it would be wise to copy the found= line into the loop, but that is not so!  If you did that, then the script would always return found: 0.  I think the best thing to do here is to create a function:found () {    grep -co [[:space:]]$1/ /etc/services}but now the exit value of this function can directly work as a condition. You'll want to quiet the output so it doesn't appear along with yours, and then you'll no longer need the -co flags, but you will want -q:while found $port; doThen, to return the number of occurrences of the input port, it would beecho found: $(found $1)or you could use printf.Putting it all togetherI think this is the script you were trying to write:#!/bin/shfound () {    grep -q [[:space:]]$1/ /etc/services}port=$1while found $port; do    port=$(($port+1))doneprintf 'found: %d\nport %d\n' $(found $1) $portBonusAs I was writing this, my initial reaction was that counting lines matching a given pattern could be done in awk as well as grep.  It then occurred to me that the entire program could be written in awk.  As a bonus, here is one implementation of that (though here I assume that /etc/services is sorted by port number):#!/usr/bin/awk -fBEGIN {    FS = [ \t/][ \t/]*    ARGC = 2    PORT = ARGV[1]    OPORT = PORT    ARGV[1] = /etc/services}($2 == OPORT) {    FOUND = FOUND + 1}(NEXT == 0 && NR > 1 && $2 > PORT) {    NEXT = ($2 > PORT + 1) ? PORT + 1 : NEXT    PORT = $2}END {    printf found: %d\nport: %d\n, FOUND, (FOUND == 0) ? OPORT : NEXT}