J V
J V

Reputation: 11936

How can you run a command in bash over and over until success?

I have a script and want to ask the user for some information, but the script cannot continue until the user fills in this information. The following is my attempt at putting a command into a loop to achieve this but it doesn't work for some reason:

echo "Please change password"
while passwd
do
    echo "Try again"
done

I have tried many variations of the while loop:

while `passwd`
while [[ "`passwd`" -gt 0 ]]
while [ `passwd` -ne 0 ]]
# ... And much more

But I can't seem to get it to work.

Upvotes: 366

Views: 237848

Answers (8)

link89
link89

Reputation: 1763

I prefer for-loop (with a large number) over while-loop. My solution is to use for-loop with bash and Here document. For example,

#!/bin/bash
set -e

for count in $(seq 1 5000); do bash <<EOF && break
echo "try $count ..."
false
EOF
done && echo ok || echo err

It's easy to extend this method to run command remotely or inside a container, for example,

#!/bin/bash
set -e

for count in $(seq 1 5000); do ssh remote-host bash <<EOF && break
echo "try $count ..."
hostname && false
EOF
done && echo ok || echo err

Upvotes: 0

sekrett
sekrett

Reputation: 1275

It becomes a little tricky if you want the strict mode (set -e) and none of above worked.

set -euo pipefail

counter=0
max_attempts=3
while ret=0; <your command> || ret=$?; [ $ret -ne 0 ]
do
  [[ $counter -eq $max_attempts ]] && echo "Command failed"; exit 1
  sleep 2
  echo "Trying again"
  counter=$((counter+1))
done

Upvotes: 1

duckworthd
duckworthd

Reputation: 15207

To elaborate on @Marc B's answer,

$ passwd
$ while [ $? -ne 0 ]; do !!; done

Is a nice way of doing the same thing that's not command specific.

If you want to do this as an alias (kudos to @Cyberwiz):

alias rr='while [ $? -ne 0 ]; do eval $(history -p !!); done'

Usage:

$ passwd
$ rr

Upvotes: 132

aclowkay
aclowkay

Reputation: 3877

If anyone looking to have retry limit:

max_retry=5
counter=0
until <YOUR_COMMAND>
do
   sleep 1
   [[ counter -eq $max_retry ]] && echo "Failed!" && exit 1
   echo "Trying again. Try #$counter"
   ((counter++))
done

Upvotes: 34

Erik
Erik

Reputation: 91270

until passwd
do
  echo "Try again"
done

or

while ! passwd
do
  echo "Try again"
done

Upvotes: 577

kurumi
kurumi

Reputation: 25599

You can use an infinite loop to achieve this:

while true
do
  read -p "Enter password" passwd
  case "$passwd" in
    <some good condition> ) break;;
  esac
done

Upvotes: 10

Andr&#233;s Rivas
Andr&#233;s Rivas

Reputation: 76

while [ -n $(passwd) ]; do
        echo "Try again";
done;

Upvotes: 2

Marc B
Marc B

Reputation: 360612

You need to test $? instead, which is the exit status of the previous command. passwd exits with 0 if everything worked ok, and non-zero if the passwd change failed (wrong password, password mismatch, etc...)

passwd
while [ $? -ne 0 ]; do
    passwd
done

With your backtick version, you're comparing passwd's output, which would be stuff like Enter password and confirm password and the like.

Upvotes: 117

Related Questions