0

I'm writing some shell script which asks the user for a domain name. I'd like to prompt the user for input in a loop, and write an error message, if the input is not a valid domain name.

Basically, the code shall look like this:

#!/bin/bash
 
read -p "Enter domain name: " DOMAIN_NAME

while [[ testing for valid domain name goes here ]]
do
  echo ''
  echo 'You entered an invalid domain name. Please re-enter.'
  echo '... more error message data ....'
  echo ''
  read -p "Enter domain name: " DOMAIN_NAME
done

echo "You entered domain name: $DOMAIN_NAME"

I've found some examples that show how the "=~" operator can be used to test against a reqular expression. However, the examples all show UNTIL loops. Using the regex from the answer to question Check valid (sub)domain with regex in bash, an example is:

# !/bin/bash

until [[ "$DOMAIN_NAME" =~ ^([a-zA-Z0-9](([a-zA-Z0-9-]){0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,5}$ ]]
do
  read -p "Enter domain name: " DOMAIN_NAME
done

echo "You entered domain name: $DOMAIN_NAME"

While this works, it is not straitforward to write an additional error message in case the user entered invalid data. Such a message would have to be written inside the loop, before the read, but not on first iteration.

For this reason I'd rather write the loop as a while loop as shown above. I'm unable to find how I can negate the test expresssion, which is needed when rewriting the until loop as a while loop.

2
  • 1
    until ! [[ $foo =~ bar ]]
    – jesse_b
    Commented Feb 24, 2023 at 16:02
  • So simple, of course! From reading examples, such as [^a-z] which matches any character except from a-z, I was blinded and searched for the negation of the regular expression. Thanks.
    – phunsoft
    Commented Feb 24, 2023 at 18:30

1 Answer 1

6

until cmd is the same as while ! cmd.

Here, just do:

until
  IFS= read -rep 'Enter domain name: ' domain
  [[ $domain =~ $regexp ]]
do
  printf>&2 'Invalid domain: "%s"\n' "$domain"
done

printf 'You entered: "%s"\n' "$domain"

Same as:

while
  IFS= read -rep 'Enter domain name: ' domain
  ! [[ $domain =~ $regexp ]]
do
  printf>&2 'Invalid domain: "%s"\n' "$domain"
done

printf 'You entered: "%s"\n' "$domain"

Or:

while
  IFS= read -rep 'Enter domain name: ' domain
  [[ ! $domain =~ $regexp ]]
do
  printf>&2 'Invalid domain: "%s"\n' "$domain"
done

printf 'You entered: "%s"\n' "$domain"

Note that we have the read in the condition part of the while/until loop so it's run each time before the regexp test, but we don't check its exit status. It's the last command run in the condition part (here [[ ... ]] or ! [[ ... ]]) that determines the branching of the loop.

Also remember you can't use character ranges for input validation unless you fix the locale to C or switch to a shell like zsh where ranges are based on code points.

#! /bin/zsh -
set -o extendedglob
until
  domain=
  vared -p 'Enter domain name: ' domain
  [[ $domain = ([a-zA-Z0-9]([a-zA-Z0-9-](#c0,61)[a-zA-Z0-9]|).)##[a-zA-Z](#c2,5) ]]
do
  printf>&2 'Invalid domain: "%s"\n' "$domain"
done
printf 'You entered: "%s"\n' "$domain"

(here using the vared (var editor) builtin to be able to use zsh's line editor like bash's read -e).

If using =~ to do regexp matching in zsh (as opposed to glob pattern matching with =), you'd want to make sure you use PCREs by using the rematchpcre option. By default, you get EREs like in bash with all its limitations. But remember that PCRE's equivalent of ERE's $ is \z. So:

regexp='^([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,5}\z'

Beware that in UTF-8 locales, sequences of bytes not forming valid characters would trigger PCRE errors. Those are handled more gracefully when using zsh glob patterns.

In bash, outside the C locale, you need [0123456789] instead of [0-9] if you want to match on only those 10 characters (with or without the globasciiranges option enabled). Same for [a-z] or [A-Z]. At least in bash, variables can't contain the NUL byte, so you don't have to worry about regexps not being able to match on those.

Also remember:

  • using read without -r and without IFS= hardly ever makes sense in bash.
  • echo shouldn't be used to output arbitrary data.
  • errors should go to stderr
1
  • Thanks for the examples showing multiple command in the contition part. Didn't know that.
    – phunsoft
    Commented Feb 24, 2023 at 18:54

You must log in to answer this question.

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