paninindicuaro
paninindicuaro

Reputation: 25

bash: while loop fails to exit when condition is met

I'm new to bash, and the below while loop is driving me crazy. I want the loop to exit when the user enters y or n but it fails to do so. I have tried different ways but no luck. Anyone can point me in the right direction?

    echo "want to go for a walk Y/N "
    read answer
    while [ "$answer" != "y" ]  ||  [ "$answer" != "n" ]  ; do 
               
        echo "Enter y or n"
        read answer
    done

The second solution also is in the same scenario

    echo "want to go for a walk Y/N "
    read answer
    while [ "$answer" != "y"   ||   "$answer" != "n" ]  ; do 
               
        echo "Enter y or n"
        read answer
    done

Upvotes: 0

Views: 1758

Answers (2)

kfkhalili
kfkhalili

Reputation: 1035

Of course it won't exit. That loop never will.

In your code, answer is always NOT 'y' OR NOT 'n' (together, at the same time).

It is so because if you select 'y' then it's NOT 'n' and if you select 'n' then it's NOT 'y'.

If you have TRUE || FALSE = TRUE And if you have FALSE || TRUE = TRUE, so the loop keeps going.

The condition you want to use is && (AND), so while answer is NOT 'y' AND NOT 'n' then go on, but if it is one of them then exit.

TRUE && FALSE => FALSE and FALSE && TRUE => FALSE and that's what you need for the loop to finally end.

#!/bin/bash

echo "want to go for a walk y/n "
read -r answer
while [ "$answer" != "y" ]  &&  [ "$answer" != "n" ]  ; do 
           
    echo "Enter y or n"
    read -r answer
done

The -r flag in read -r var is to avoid reading backslash as an escape character. It is not relevant for this toy problem but it is a best practice to include it.

Comment from David: Keep in mind that the code doesn't handle case, so Y is not recognized as y.

Upvotes: 2

Léa Gris
Léa Gris

Reputation: 19545

Don't forget in a while loop, the first commands list may also contain multiple statements:

#!/usr/bin/env sh

echo "want to go for a walk y/n "

while
  read -r answer
  case $answer in
    [yYnN]) false ;;
  esac
do
  echo "Enter y or n"
done

Or using until and Bash's Extended Regular Expression:

#!/usr/bin/env bash

echo "want to go for a walk y/n "

until
  read -r answer
  [[ $answer =~ [yYnN] ]]
do
  echo "Enter y or n"
done

And a featured, case-insensitive POSIX compatible implementation:

#!/usr/bin/env sh

echo "want to go for a walk y/n "

until
  read -r answer
  case $answer in
    [yY])
      echo "Handling Yes answer stuffs"
      ;;
    [nN])
      echo "Stuffs for No answer"
      ;;
    *)      # Landing here if invalid answer
      false # Proceed with the do...done statements
      ;;
  esac
do
  echo "Enter y or n"
done

Upvotes: 0

Related Questions