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

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

Related

Brace expansion of {1..$n} [duplicate]

This question already has answers here:
Brace expansion with a Bash variable - {0..$foo}
(5 answers)
Closed 4 years ago.
I am trying to loop from 1 to n where n is from user input.
If I do:
read n
echo {1..$n}
I get this output for the input 5
{1..5}
How do I make it expand to
1 2 3 4 5
Keep it simple by trying to do it with a for loop as follows.
echo "enter number..."
read n
for((i=1;i<=n;i++)); do
echo "$i"
done
Or use seq with for loop as follows too.
echo "Enter number:"
read howmany
for i in $(seq 1 $howmany); do
echo "$i";
done
Curly braces don't support variables in bash, though eval could be used but it is evil and have loopholes, why so see this link carefully http://mywiki.wooledge.org/BashFAQ/048

Variable is not being incremented in bash function [duplicate]

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.

UNTIL LOOP in shell for read file [duplicate]

This question already has answers here:
Read file for value, loop until value = $foo?
(3 answers)
Closed 5 years ago.
#!/bin/bash
i=1
cat days.txt | while read days
do
echo $i $days
let i++
done
I want to change while loop into until loop
#!/bin/bash
i=1
until conditions
do
echo $i $days
let i++
done
expected result
Monday
Tuesday
Wednesday
Blah Blah Blah
while read can be written more awkwardly as until ! read:
i=1
until ! read -r days; do
echo "$i $days"
i=$(( i + 1 ))
done < file
while command does something as long as command exits successfully, whereas until command does something until command exits unsuccessfully. The ! is used to negate the exit code of read.

read line by line using `for` loop

How can I read line by line using for loop?
what I've tried:
hh=$(echo -e "\n1\n2\n3\n4\n")
IFS=$'\n';
for r in "$hh"; do echo $r; done
1 2 3 4
echo -e "$hh"
1
2
3
4
Use a while loop:
$ while read -r r; do echo $r; done <<< "$hh"
1
2
3
4
The correct answer is, you don't read line-by-line with a for loop. Use a while loop instead with the read builtin:
while IFS= read -r line; do
echo "$line"
done <<< "$hh"
Though using while read is definitely can solve this but if you really want to use for loop then you need to use IFS=$'\n' to read your input string in the bash's for loop:
hh=$(echo -e "\nname n1\nval n2\n3\nfoo n4\n")
IFS=$'\n' && for r in $hh; do echo "r='$r'"; done
r='name n1'
r='val n2'
r='3'
r='foo n4'
Remove the quotes from around $hh and the original code works fine:
hh=$(echo -e "\n1\n2\n3\n4\n")
IFS=$'\n'
for r in $hh; do echo "Value: $r"; done
# output:
Value: 1
Value: 2
Value: 3
Value: 4
It's only the expansion of the $hh variable in the for loop that DOES NOT need quoting.
This code works.
hh=$(echo -e "\n1\n2\n3\n4\n")
echo "Starting string"
echo -e "$hh"
IFS=$'\n';
echo "Original Code"
for r in "$hh"; do echo r is $r; done
echo "Fixed Code 1"
IFS=$'\n';
for r in "$hh"; do echo r is "$r"; done
echo "Fixed Code 2"
IFS=$'\n';
for r in $hh ; do echo r is $r ; done
And produces,
Starting string
1
2
3
4
Original Code
r is 1 2 3 4
Fixed Code 1
r is
1
2
3
4
Fixed Code 2
r is 1
r is 2
r is 3
r is 4

Bash Script - Scope of variables in while loop [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Bash Script - Variable Scope in Do-While loop
In the following code, it prints the correct value of i in inner while loop, but it prints 0 after it comes out of inner while loop:
string="Medicine"
for file in *
do
i=0
cat $file | while read line
do
echo $line
if [ "$line" == "$string" ];
then
i=`expr $i + 1`
echo $i
fi
done
echo $i
done
By default variables have global scope in shell scripting, which make "i " global. if you want to make a variable as local ,use keyword "local" , for eg: local i=0
for more details check the link, http://www.bashguru.com/2008/06/bash-variables-scope.html

Resources