loop in bash does not perform integer comparison properly - bash

I have below code
i=0
for s in / - \\ \|
do
printf "\rWaiting for application start to finish $i $s"
sleep 1
((i++))
if [[ $i -gt 30 ]]
then
break
fi
done
The loop always ends after 3 iterations. Any reason as why?

This might be what you are looking for:
#!/bin/bash
a=('/' '-' '\' '|')
for ((i = 0; i < 30; ++i)); do
printf '\rWaiting for application start to finish %d %s' \
"$i" "${a[i%4]}"
sleep 1
done
echo

My mistake. The for loop which has three arguments is exiting instead of the if condition failing.

Related

Difference between continue and colon in conditional statement

What is the difference between using a colon, which means "do nothing" and continue, which means skip.
if [[ -s $file ]] ; then
:
fi
if [[ -s $file ]] ; then
continue
fi
: is a synonym for true. It does not prevent later commands in the same block or loop from running.
Compare:
for (( i=0; i<3; i++ )); do
echo "Starting iteration $i"
(( i == 1 )) && { echo " About to run :"; :; echo " Just ran :"; }
(( i == 2 )) && { echo " About to run continue"; continue; echo " Just ran continue"; }
echo "Ending iteration $i"
done
Our output is:
Starting iteration 0
Ending iteration 0
Starting iteration 1
About to run :
Just ran :
Ending iteration 1
Starting iteration 2
About to run continue
Note that we made it to "ending" after running :, but not after running continue.
It depends on your program's logic.
Outside of a loop you get
$ continue
bash: continue: only meaningful in a `for', `while', or `until' loop

Limit bash until loop to 3 retries [duplicate]

How can this while loop be limited to maximum 10 retries?
#!/bin/sh
while ! test -d /somemount/share/folder
do
echo "Waiting for mount /somemount/share/folder..."
sleep 1
done
Keep a counter:
#!/bin/sh
while ! test -d /somemount/share/folder
do
echo "Waiting for mount /somemount/share/folder..."
((c++)) && ((c==10)) && break
sleep 1
done
You can also use a for loop and exit it on success:
for try in {1..10} ; do
[[ -d /somemount/share/folder ]] && break
done
The problem (which exists in the other solutions, too) is that once the loop ends, you don't know how it ended - was the directory found, or was the counter exhausted?
I would comment but I do not have enough points for that. I want to contribute anyway.
So this makes it work even if the while loop is nested in another loop. before the break the c variable is being reset to zero.
credits to #anubhava who came up with the original solution.
#!/bin/sh
while ! test -d /somemount/share/folder
do
echo "Waiting for mount /somemount/share/folder..."
((c++)) && ((c==10)) && c=0 && break
sleep 1
done
You can use until (instead of "while ! ... break), with a counter limit:
COUNT=0
ATTEMPTS=10
until [[ -d /somemount/share/folder ]] || [[ $COUNT -eq $ATTEMPTS ]]; do
echo -e "$(( COUNT++ ))... \c"
sleep 1
done
[[ $COUNT -eq $ATTEMPTS ]] && echo "Could not access mount" && (exit 1)
Notes:
Just like setting counter as variable, you can set var condition="[[ .. ]]", and use until eval $condition to make it more generic.
echo $(( COUNT++ )) increases the counter while printing.
If running inside a function, use "return 1" instead of "exit 1".

Counting down in a loop to zero by the number being given

I am trying to write a while loop to determine the number is being given to count down to 0. Also, if there's no argument given, must display "no parameters given.
Now I have it counting down but the last number is not being 0 and as it is counting down it starts with the number 1. I mush use a while loop.
My NEW SCRIPT.
if [ $# -eq "0" ] ;then
echo "No paramters given"
else
echo $#
fi
COUNT=$1
while [ $COUNT -gt 0 ] ;do
echo $COUNT
let COUNT=COUNT-1
done
echo Finished!
This is what outputs for me.
sh countdown.sh 5
1
5
4
3
2
1
Finished!
I need it to reach to 0
#Slizzered has already spotted your problem in a comment:
You need operator -ge (greater than or equal) rather than -gt (greater than) in order to count down to 0.
As for why 1 is printed first: that's simply due to the echo $# statement before the while loop.
If you're using bash, you could also consider simplifying your code with this idiomatic reformulation:
#!/usr/bin/env bash
# Count is passed as the 1st argument.
# Abort with error message, if not given.
count=${1?No parameters given}
# Count down to 0 using a C-style arithmetic expression inside `((...))`.
# Note: Increment the count first so as to simplify the `while` loop.
(( ++count ))
while (( --count >= 0 )); do
echo $count
done
echo 'Finished!'
${1?No parameters given} is an instance of shell parameter expansion
bash shell arithmetic is documented here.
You should also validate the variable before using it in an arithmetic context. Otherwise, a user can construct an argument that will cause the script to run in an infinite loop or hit the recursion limit and segfault.
Also, don't use uppercase variable names since you risk overriding special shell variables and environment variables. And don't use [ in bash; prefer the superior [[ and (( constructs.
#!/usr/bin/env bash
shopt -s extglob # enables extended globs
if (( $# != 1 )); then
printf >&2 'Missing argument\n'
exit 1
elif [[ $1 != +([0-9]) ]]; then
printf >&2 'Not an acceptable number\n'
exit 2
fi
for (( i = $1; i >= 0; i-- )); do
printf '%d\n' "$i"
done
# or if you insist on using while
#i=$1
#while (( i >= 0 )); do
# printf '%d\n' "$((i--))"
#done
Your code is far from being able to run. So, I don't know where to start to explain. Let's take this small script:
#!/bin/sh
die() {
echo $1 >&2
exit 1;
}
test -z "$1" && die "no parameters given"
for i in $(seq $1 -1 0); do
echo "$i"
done
The main part is the routine seq which does what you need: counting from start value to end value (with increment in between). The start value is $1, the parameter to our script, the increment is -1.
The test line tests whether there is a parameter on the command line - if not, the script ends via the subroutine die.
Hth.
There are a number of ways to do this, but the general approach is to loop from the number given to an ending number decrementing the loop count with each iteration. A C-style for loop works as well as anything. You will adjust the sleep value to get the timing you like. You should also validate the required number and type of input your script takes. One such approach would be:
#!/bin/bash
[ -n "$1" ] || {
printf " error: insufficient input. usage: %s number (for countdown)\n" "${0//*\//}"
exit 1
}
[ "$1" -eq "$1" >/dev/null 2>&1 ] || {
printf " error: invalid input. number '%s' is not an integer\n" "$1"
exit 1
}
declare -i cnt=$(($1))
printf "\nLaunch will occur in:\n\n"
for ((i = cnt; i > 0; i--)); do
printf " %2s\n" "$i"
sleep .5
done
printf "\nFinished -- blastoff!\n\n"
exit 0
Output
$ bash ./scr/tmp/stack/countdown.sh 10
Launch will occur in:
10
9
8
7
6
5
4
3
2
1
Finished -- blastoff!
Your Approach
Your approach is fine, but you need to use the value of COUNT $COUNT in your expression. You also should declare -i COUNT=$1 to tell the shell to treat it as an integer:
#!/bin/bash
if [ $# -eq "0" ] ;then
echo "No paramters given"
else
echo -e "\nNumber of arguments: $#\n\n"
fi
declare -i COUNT=$1
while [ $COUNT -gt 0 ] ;do
echo $COUNT
let COUNT=$COUNT-1
done
echo -e "\nFinished!\n"

Bash Next While plus for loop

Looking for guidance on my while loop and how to get it to actually have a countdown and then checks the query status again etc... any guidance? Right now I'm looking to see if I can get it to count down from 59 to zero...
STATUS='DONE'
QUERY_STATUS=$(curl .....)
while [ "$STATUS" != "$QUERY_STATUS" ]; do
for (( i=60; i>0; i--)); do
printf "\rWaiting for Query to finish, will check back in $i seconds"
i=$((i + 1))
done
QUERY_STATUS=$(curl .....)
done
#!/bin/bash
STATUS='DONE'
while true; do
QUERY_STATUS=$(curl …) # You can just do this once inside the loop
# and exit the loop with a guard
[[ $STATUS = $QUERY_STATUS ]] && break
for i in {60..1}; do # You had i-- here, but i + 1 elsewhere
# Might as well use `printf` the way it was meant to be used ;)
printf '\rWaiting for Query to finish, will check back in %d seconds' "$i"
sleep 1 # You weren't actually sleeping inside the loop.
done
done
I was able to get the following to work:
STATUS='DONE'
QUERY_STATUS=$(curl .....)
while [ "$STATUS" != "$QUERY_STATUS" ]; do
for (( i=60; i>0; )); do
printf "\rWaiting for Query to finish, will check back in $i seconds"
sleep 1;
i=$((i-1))
done
QUERY_STATUS=$(curl ....)
done

Cannot debug simple ksh programme

I wrote this sample KornShell (ksh) code but it is getting bad substitution error during the if clause.
while ((i < $halflen))
do
if [[${strtochk:i:i}==${strtochk:j:j}]];then
i++
j--
else
ispalindrome = false
fi
done
Please help.
NB: I am using ksh88, not ksh93.
shell syntax is very whitespace sensitive:
[[ is acually the name of a command, it's not just syntax, so there must be a space following it.
The last argument of [[ must be ]], so it needs to be preceded by a space.
[[ works differently depending on the number of arguments it receives, so you want to have spaces around ==
In a variable assignment, you must not have spaces around =.
Tips:
once you figure out it's not a palindrome, break out of the while loop
you are probably checking character by character, so you want ${strtochk:i:1}
i++ and j-- are arithmetic expressions, not commands, so you need the double parentheses.
are you starting with i=0 and j=$((${#strtochk} - 1))?
while ((i < halflen))
do
if [[ ${strtochk:i:1} == ${strtochk:j:1} ]];then
((i++))
((j--))
else
ispalindrome=false
break
fi
done
Check if your system has rev, then you can simply do:
if [[ $strtochk == $( rev <<< "$strtochk" ) ]]; then
echo "'$strtochk' is a palindrome"
fi
function is_palindrome {
typeset strtochk=$1
typeset -i i=1 j=${#strtochk}
typeset -i half=$(( j%2 == 1 ? j/2+1 : j/2 ))
typeset left right
for (( ; i <= half; i++, j-- )); do
left=$( expr substr "$strtochk" $i 1 )
right=$( expr substr "$strtochk" $j 1 )
[[ $left == $right ]] || return 1
done
return 0
}
if is_palindrome "abc d cba"; then
echo is a palindrome
fi
You are using ksh88 but the code you tried is using ksh93 feature missing for the 88 version.
You need to replace
if [[${strtochk:i:i}==${strtochk:j:j}]];then
with these portable lines:
if [ "$(printf "%s" "$strtochk" | cut -c $i)" =
"$(printf "%s" "$strtochk" | cut -c $j)" ]; then
and the incorrect:
i++
j--
with:
i=$((i+1))
j=$((j-1))

Resources