Counter Increment in Bash Loop Not Working

Counter increment in Bash loop not working

First, you are not increasing the counter. Changing COUNTER=$((COUNTER)) into COUNTER=$((COUNTER + 1)) or COUNTER=$[COUNTER + 1] will increase it.

Second, it's trickier to back-propagate subshell variables to the callee as you surmise. Variables in a subshell are not available outside the subshell. These are variables local to the child process.

One way to solve it is using a temp file for storing the intermediate value:

TEMPFILE=/tmp/$$.tmp
echo 0 > $TEMPFILE

# Loop goes here
# Fetch the value and increase it
COUNTER=$[$(cat $TEMPFILE) + 1]

# Store the new value
echo $COUNTER > $TEMPFILE

# Loop done, script done, delete the file
unlink $TEMPFILE

Incrementing a variable inside a Bash loop

You are using USCOUNTER in a subshell, that's why the variable is not showing in the main shell.

Instead of cat FILE | while ..., do just a while ... done < $FILE. This way, you avoid the common problem of I set variables in a loop that's in a pipeline. Why do they disappear after the loop terminates? Or, why can't I pipe data to read?:

while read country _; do
if [ "US" = "$country" ]; then
USCOUNTER=$(expr $USCOUNTER + 1)
echo "US counter $USCOUNTER"
fi
done < "$FILE"

Note I also replaced the `` expression with a $().

I also replaced while read line; do country=$(echo "$line" | cut -d' ' -f1) with while read country _. This allows you to say while read var1 var2 ... varN where var1 contains the first word in the line, $var2 and so on, until $varN containing the remaining content.

Incrementing in Bash shell script not showing the changed values?

You are using the $sign in a left assignment statement, which is not what you expect.
Please change your line

$a =  `expr $a + 1` ; 

to

a=`expr $a + 1`;

And also be warned that spaces before and after = sign shouldn't be used in bash scripts

UPDATE:
This code does work without syntax errors with bash or sh:

a=23
while [ $a -lt 45 ]; do
a=`expr $a + 1`
echo "the new value is $a"
done

And prints:

the new value is 24
the new value is 25
the new value is 26
the new value is 27
the new value is 28
the new value is 29
the new value is 30
the new value is 31
the new value is 32
the new value is 33
the new value is 34
the new value is 35
the new value is 36
the new value is 37
the new value is 38
the new value is 39
the new value is 40
the new value is 41
the new value is 42
the new value is 43
the new value is 44
the new value is 45

Bash Loop with counter gives a count of 1 when no item found. Why?

If you've got Bash 4.0 or later you can use globstar instead of (the error-prone) find. Try this Shellcheck-clean code:

#! /bin/bash -p

shopt -s dotglob extglob nullglob globstar

function create_tifs
{
local dir dtfile
local -i count
for dir in */; do
printf '\nFolder processed: %s\n' "$dir" >&2

count=0
for dtfile in "$dir"**/*-DT-!(*.jpg); do
if [[ $dtfile == *.tif ]]; then
printf 'TIF already done %s\n' "$dtfile" >&2
else
cp -v -n -- "$dtfile" "$dtfile".tif
count+=1
fi
done
printf 'Count = %d\n' "$count" >&2
done

return 0
}
  • shopt -s ... enables some Bash settings that are required by the code:
    • dotglob enables globs to match files and directories that begin with .. find shows such files by default.
    • extglob enables "extended globbing" (including patterns like !(*.jpg)). See the extglob section in glob - Greg's Wiki.
    • nullglob makes globs expand to nothing when nothing matches (otherwise they expand to the glob pattern itself, which is almost never useful in programs).
    • globstar enables the use of ** to match paths recursively through directory trees.
  • Note that globstar is potentially dangerous in versions of Bash prior to 4.3 because it follows symlinks, possibly leading to processing the same file or directory multiple times, or getting stuck in a cycle.
  • The -v option with cp causes it to print details of what it does. You might prefer to drop the option and print a different format of message instead.
  • See the accepted, and excellent, answer to Why is printf better than echo? for an explanation of why I used printf instead of echo.
  • I didn't use cd because it often leads to problems in programs.

bash: a for loop does not increment by 1. How can I treat it like it does in order to get the index (1, 2, 3, etc) of each loop?

You can use multiple variables per arithmetic for loop:

ALLSTEPS=820000
for ((i=1, step=20000; step <= ALLSTEPS; i++, step+=20000)); do
echo "Step: $step, i: $i"
done

Need to not increment a loop when a condition is met

You can just wrap your read and case block inside a while loop that doesn't end until a valid answer is given:

for sac in $event/*R.sac; do
***preprocess the data***

while true; do
read -p "Is this a good SKS event? Type 'q1' for a really good event, 'q2' for an okay event, 'ns' for a null split, or 'be' for a bad event: " -n 2 answer
echo

# Start by assuming answer is good
answer_ok=1

case "$answer" in
# (cases for valid answers omitted for clarity)

* )
echo -e "\nWrong input\nTry again\n"

# Flag answer as bad
answer_ok=0
;;
esac

# If user gave a good answer, break out of the while loop
if (( answer_ok )); then
break
fi
done
done

An alternative and perhaps cleaner method of terminating the while loop (depending on how many cases you have) would be to just add a break in each valid case, just before the ;;. This would break out of the while loop. But the *) case wouldn't have a break, so if it hits that case it will just loop back around again.

For example:

while true; do
read -p "Is this a good SKS event? Type 'q1' for a really good event, 'q2' for an okay event, 'ns' for a null split, or 'be' for a bad event: " -n 2 answer
echo

case "$answer" in
q1) # do stuff
break
;;
q2) # do stuff
break
;;
ns) # do stuff
break
;;
be) # do stuff
break
;;
* )
echo -e "\nWrong input\nTry again\n"
# No break - loop back around again
;;
esac
done

Yet another method would be to re-check the answer after the case block, just before the end of the while loop:

while true; do
read -p "Is this a good SKS event? Type 'q1' for a really good event, 'q2' for an okay event, 'ns' for a null split, or 'be' for a bad event: " -n 2 answer
echo

case "$answer" in
q1) # do stuff
;;
q2) # do stuff
;;
ns) # do stuff
;;
be) # do stuff
;;
* )
echo -e "\nWrong input\nTry again\n"
;;
esac

# If "$answer" is valid, break out of the loop.
if [[ "$answer" =~ ^(q1|q2|ns|be)$ ]]; then
break
fi
done

The =~ is a bash-specific operator that performs a regular expression match.

Continuing with this approach, you could use the negated regular expression test as the conditional of the while loop (after first initializing answer to an invalid answer, so that the conditional will fail the very first time).

answer=foo
while [[ ! "$answer" =~ ^(q1|q2|ns|be)$ ]]; do
read -p "Is this a good SKS event? Type 'q1' for a really good event, 'q2' for an okay event, 'ns' for a null split, or 'be' for a bad event: " -n 2 answer
echo

case "$answer" in
q1) # do stuff
;;
q2) # do stuff
;;
ns) # do stuff
;;
be) # do stuff
;;
* )
echo -e "\nWrong input\nTry again\n"
;;
esac
done

Increment in bash loop by set amount

If you want to print the ranges within 773, you can do like this

#!env bash
start=1
end=19
for counter in {1..773}
do
echo $counter. "\$start = " $start " and \$end = " $end
if [[ $start -eq 1 ]];
then
start=0
fi
start=$(($start+20))
end=$(($end+20))
if [[ $end -ge 773 ]];
then
break
fi
done

Output

1. $start =  1  and $end =  19
2. $start = 20 and $end = 39
3. $start = 40 and $end = 59
4. $start = 60 and $end = 79
5. $start = 80 and $end = 99
6. $start = 100 and $end = 119
7. $start = 120 and $end = 139
8. $start = 140 and $end = 159
9. $start = 160 and $end = 179
10. $start = 180 and $end = 199
11. $start = 200 and $end = 219
12. $start = 220 and $end = 239
13. $start = 240 and $end = 259
14. $start = 260 and $end = 279
15. $start = 280 and $end = 299
16. $start = 300 and $end = 319
17. $start = 320 and $end = 339
18. $start = 340 and $end = 359
19. $start = 360 and $end = 379
20. $start = 380 and $end = 399
21. $start = 400 and $end = 419
22. $start = 420 and $end = 439
23. $start = 440 and $end = 459
24. $start = 460 and $end = 479
25. $start = 480 and $end = 499
26. $start = 500 and $end = 519
27. $start = 520 and $end = 539
28. $start = 540 and $end = 559
29. $start = 560 and $end = 579
30. $start = 580 and $end = 599
31. $start = 600 and $end = 619
32. $start = 620 and $end = 639
33. $start = 640 and $end = 659
34. $start = 660 and $end = 679
35. $start = 680 and $end = 699
36. $start = 700 and $end = 719
37. $start = 720 and $end = 739
38. $start = 740 and $end = 759


Related Topics



Leave a reply



Submit