Variable is not being incremented in bash function [duplicate] - bash

This question already has answers here:
A variable modified inside a while loop is not remembered
(8 answers)
Closed 4 years ago.
I have this:
ql_remove_locks(){
local pid="$$";
declare -i count=0;
ql_pid="$pid" ql_node_ls_all | while read line; do
count=$((count+1));
echo "count: $count";
echo "deleting lock: $line";
rm -rf "$line";
done;
echo "quicklock: $count lock(s) removed."
}
I am getting this output:
count: 1
deleting lock: /Users/alexamil/.quicklock/locks/a.lock
quicklock: 0 lock(s) removed.
I have tried so many things, still 0 always gets logged:
quicklock: 0 lock(s) removed.
Why is the count 0 instead of 1 in the echo statement?

Commands in a pipeline are run in subshells. The while loop is in a subshell, with its own copies of variables, and modifying $count there doesn't propagate back to the parent shell.
You can switch the pipe to a redirection + process substitution to work around this.
while read line; do
count=$((count+1))
echo "count: $count"
echo "deleting lock: $line"
rm -rf "$line"
done < <(ql_pid="$pid" ql_node_ls_all)
NB: The semicolons are superfluous.

Related

why bash read command create a loop-local variable? [duplicate]

This question already has answers here:
A variable modified inside a while loop is not remembered
(8 answers)
How to pipe input to a Bash while loop and preserve variables after loop ends
(3 answers)
Closed 2 days ago.
I wrote following bash script. The read command creates variable var.
if $var==5 then break loop.
seq 10 | while read -r var
do
echo "read: $var"
if [[ $var == 5 ]]; then
echo "break loop: $var"
break
fi
done
echo "after loop: $var"
Here is the output
read: 1
read: 2
read: 3
read: 4
read: 5
break loop: 5
after loop:
My question is: why after loop, $var is empty?
The variable isn't loop-local, and everything related to read and loops here are just red herrings. The behavior you're seeing is because the pipe starts a subshell. If you get rid of the pipe and run this code instead:
while read -r var
do
echo "read: $var"
if [[ $var == 5 ]]; then
echo "break loop: $var"
break
fi
done < <(seq 10)
echo "after loop: $var"
Then it will print this instead:
read: 1
read: 2
read: 3
read: 4
read: 5
break loop: 5
after loop: 5

Inserting to an shell array in a loop [duplicate]

This question already has answers here:
A variable modified inside a while loop is not remembered
(8 answers)
Closed 2 years ago.
I am trying to insert values in to an array in a loop as follows:
branches_to_delete=()
cat branches.txt | while read branch_name
do
BRANCH_COUNT=$(git ls-remote | grep -w $branch_name | wc -l)
if [ $BRANCH_COUNT -eq 0 ]; then
echo "Branch does not exist"
branches_to_delete+=($branch_name)
elif [ $BRANCH_COUNT -eq 1 ]; then
echo "Branch exists"
else
echo "Not valid result"
fi
done
echo "Loop finished"
echo ${branches_to_delete[#]}
But when I printout branches_to_delete it is actually empty.
What am I doing wrong here?
With the pipe from cat branches.txt into the read loop, you create a subshell that cannot access the parent shell's branches_to_delete array.
You can fix this by avoiding the pipe and saving a useless use of cat:
branches_to_delete=()
while read branch_name; do
...
done < branches.txt
(Make sure nothing in ... reads from read's stdin. You'll notice if something is missing).

Bash loop not adding to array [duplicate]

This question already has answers here:
A variable modified inside a while loop is not remembered
(8 answers)
Closed 2 years ago.
I am trying to add elements to an array in bash. I am querying and looping over the query.
delete_arr=()
delete_arr+=("test")
mysql -e "$sql_statement" | while read directory_name; do
if [ $counter -gt 0 ]
then
delete_arr+=($directory_name)
fi
let counter=counter+1
done
for i in "${delete_arr[*]}"; do echo "$i"; done
The only output I am getting is test. I should be getting a lot more data. The query works and the data gets added to the array in the loop however when I jump out of the loop the array only contains test.I am doing nothing to reset the array.
In the pipe, you are implicitly creating a new subshell with its own namespace. Try avoiding the pipe:
delete_arr=()
delete_arr+=("test")
while read directory_name
do
if [ "$counter" -gt 0 ]
then
delete_arr+=("$directory_name")
fi
counter=$((counter+1))
done < <(mysql -e "$sql_statement")
for i in "${delete_arr[#]}"; do echo "$i"; done

bash - setting variable value is not working [duplicate]

This question already has answers here:
A variable modified inside a while loop is not remembered
(8 answers)
Closed 5 years ago.
Problem
My bash script loops through a bunch of files and if it finds an unexpected file that contains some text, it sets an error flag and is supposed to exit the loop. The problem is that when i evaluate the error condition after the loop, the error flag isn't set properly.
I can't seem to see where the bug is.
Code
#!/bin/sh
valid=1
grep -rs -L 'bogustext' * | while read line; do
echo "Processing file '$line'"
if [ "$line" != "arc/.keep" ] && [ "$line" != "arc/prp/lxc" ] && [ "$line" != "arc/lbb/lxc" ]; then
valid=0
echo "just set it to zero - $valid"
break 2
fi
done
echo "$valid"
if [ "$valid" -eq 0 ]; then
echo "1..1"
echo "not ok $line invis malformed"
else
echo "1..1"
echo "ok"
fi
Here's the output which shows the problem / bug. As you can see, there is an extra file in the list that contains the bogus string "arc/ttp". The script sets the "valid" variable to 0 but by the time I'm ready to evaluate it to display the right status, it's still the original value.
lab-1:/var/vrior# sh test.sh
Processing file 'arc/.keep'
Processing file 'arc/prp/lxc'
Processing file 'arc/lbb/lxc'
Processing file 'arc/ttp'
just set it to zero - 0
1
1..1
ok
What I've tried so far
I'm currently googling to see if in Bash there's local variables vs. global. Not too sure, but if you see my bug, please let me know.
Pipes create subshells, so $valid inside the loop refers to a new variable, which disappears before you try and use it.
Since you've tagged bash, then you can use a process substitution instead of the pipe:
while read line; do
echo "Processing file '$line'"
if [ "$line" != "arc/.keep" ] && [ "$line" != "arc/prp/lxc" ] && [ "$line" != "arc/lbb/lxc" ]; then
valid=0
echo "just set it to zero - $valid"
break 2
fi
done < <(grep -rs -L 'bogustext' *)

Variable incremented in bash while loop resets to 0 when loop finishes [duplicate]

This question already has answers here:
Local variables after loop exit
(5 answers)
Closed 6 years ago.
I'm writing a bash script that uses a while loop to process over the rows outputted from a specific command. I also increment a variable (adding 1) for each row found.
Heres an example of the section of the script in question:
#!/bin/bash
count=0
ls | while read f
do
count=$(($count+1))
echo "Count is at ${count}"
done
echo "Found total of ${count} rows"
Basically, it increments the $count variable just fine, but then when I print $count after the while loop.. its reset to 0..
Example output:
Count is at 1
Count is at 2
Count is at 3
Count is at 4
Count is at 5
Found total of 0 rows
Any idea why the $count would reset after the loops done?
I also tried adding the last echo statement using the && operator on the loop, like so:
count=0
ls | while read f
do
count=$(($count+1))
echo "Count is at ${count}"
done && echo "Found total of ${count} rows"
With no success.
Any help would be appreciated
A pipe spawns a subshell, use a process substitutions instead:
while read -r f
do
count=$(($count+1))
echo "Count is at ${count}"
done < <(ls)
Also note that you shouldn't parse the output of ls.
And your example seems to count numbers of files and directories in current directory, which can be done with find and wc:
find -maxdepth 1 -mindepth 1 -printf "\n" | wc -l
or you can avoid ls with a for loop and globbing:
for f in * .*; do
[ -e "$f" ] || continue
count=$((count + 1))
echo "Count is at ${count}"
done

Resources