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 withcp
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 ofecho
. - 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
Memory Limit to a 32-Bit Process Running on a 64-Bit Linux Os
Don't Fail Jenkins Build If Execute Shell Fails
Making Cmake Print Commands Before Executing
How to Remove the Bom from a Utf-8 File
How to Reference Files Relative to Application Root in Node.Js
Pattern Match Does Not Work in Bash Script
How to Encrypt a Large File in Openssl Using Public Key
How to Run a Cron Job Inside a Docker Container
How to Print to the Console in Color in a Cross-Platform Manner
How to Determine the Current CPU Utilization from the Shell
Get a Browser Rendered HTML+Javascript
Command Line Utility to Print Statistics of Numbers in Linux
Linux Terminal Input: Reading User Input from Terminal Truncating Lines at 4095 Character Limit
Find and Delete File or Folder Older Than X Days
How to Export a Variable in Bash
Setting Default Permissions for Newly Created Files and Sub-Directories Under a Directory in Linux