1

I have a while loop created to take user input and repeat the prompt in case of an error.

while true; do
        read -p "Enter hostname : " fqdn
        pattern="(^[^\.]*\.[^\.]*\.[^\.]*$)"
        
        if [[ -z "$fqdn" ]]; then   #if fqdn input is empty
                echo "Error! Hostname cannot be empty"
                continue
        elif [[ ! "${fqdn}" =~ ${pattern} ]]; then
                echo "Error! Format incorrect"
                continue
        else
                echo "hostname is $fqdn"
                break
        fi
done

My goal:

  1. In case of if/elif statement how to show the prompt only twice and then exit.
  2. Is there a way, the above while loop can be improved, e.g. using a case switch. I find case switch difficult to comprehend?

OS: Ubuntu 20.04 (headless)

0

2 Answers 2

2

The following bash script reads a string from the user and tests whether the string fulfills the criteria for a hostname that you seem to want to enforce.

The first of the two patterns used in the case statement tests for the pathological case of two dots with no characters in-between (this is a pattern that we don't act upon). We test for a string containing at least two dots and no dot at either end with the second pattern. The hostname variable fdqn is set to the string only if this second pattern matches the string. Note that the string foo..bar matches the second pattern, which is why we preemptively match the double dot substring with the first pattern.

#!/bin/bash

unset -v count fdqn

while true; do
        read -p 'Enter hostname: ' -r

        case $REPLY in (*..*) ;; ([!.]*.*.*[!.]) fdqn=$REPLY; break; esac

        echo 'Invalid format' >&2

        count=$(( count + 1 ))
        [ "$count" -eq 2 ] && break
done

if [ -z "$fdqn" ]; then
        echo 'No valid hostname entered' >&2
        exit 1
fi

printf 'The hostname is "%s"\n' "$fdqn"

The count variable keeps track of how many times the user has tried to enter a string. We break out of the loop if this variable's value reaches two.

There is no real need to use a case statement here unless you want to make it a portable sh script (in which case you would have to do the read statement differently). With the globbing operator == in bash, the case statement would be written like below.

if [[ $REPLY == *..* ]]; then
        :
elif [[ $REPLY == [!.]*.*.*[!.] ]]; then
        fdqn=$REPLY
        break
fi

The two tests could also be joined using && if the first is negated.

if [[ $REPLY != *..* ]] && [[ $REPLY == [!.]*.*.*[!.] ]]; then
        fdqn=$REPLY
        break
fi

If you want to avoid getting hostnames containing three dots, then make sure that $REPLY does not match *.*.*.*, just like it shouldn't match *..*.

1

Here's an alternative at the top of my mind, I'm sure it can be improved:

# declare the pattern outside the loop
pattern="(^[^.]*\.[^.]*\.[^.]*$)"
# declare a counter for failed attempts
c=0

while true; do
  # if counter is equal to 2 exit
  [[ $c -eq 2 ]] && echo "Two failed attempts. Exiting" && exit

  read -p "Enter hostname : " fqdn

  if [[ -z "$fqdn" ]]; then
    echo "Error! Hostname cannot be empty"
    ((c++)) # increment the counter
    continue
  elif [[ ! "${fqdn}" =~ ${pattern} ]]; then
    echo "Error! Format incorrect"
    ((c++)) # increment the counter
    continue
  fi
  
  # this lines will be executed only if the conditions passed
  echo "hostname is $fqdn"
  break
done

The case constructs don't use regular expressions, so for complex matching it's better to use if elif.. statements.

3
  • thnx! as for the 1st question, how to add a counter to the if/else statement so that they are broken after two attempts individually i.e. when if/elif statement is hit twice it will exit the script
    – depar
    Commented Sep 26, 2021 at 23:42
  • @depar let me see, I hope I understand you. Commented Sep 26, 2021 at 23:44
  • that was pure gold!! [[ $c -eq 2 ]] && echo "Two failed attempts. Exiting" && exit this I never imagined.
    – depar
    Commented Sep 27, 2021 at 0:06

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .