Scope of for loop control variable in shell? - linux

I have a question about for loop in shell.
Let's assume this simple shell script:
#!/bin/sh
loop() {
for i in 1 2 3 4; do
if [ $i -eq 2 ]; then
[ $1 -eq 2 ] && return 1
loop $(($1 + 1)) && return 1
fi
done
return 1
}
loop 0
All variables are global, except for arguments (and function arguments). So if I want a local variable in function
I would have to pass it as argument.
I tried to run this simple script, but I'm not sure if also the for loop list (1 2 3 4 in this example) is also local?
See below:
+ loop 0
+ for i in 1 2 3 4
+ '[' 1 -eq 2 ']'
+ for i in 1 2 3 4
+ '[' 2 -eq 2 ']'
+ '[' 0 -eq 2 ']'
+ loop 1
+ for i in 1 2 3 4
+ '[' 1 -eq 2 ']'
+ for i in 1 2 3 4
+ '[' 2 -eq 2 ']'
+ '[' 1 -eq 2 ']'
+ loop 2
+ for i in 1 2 3 4
+ '[' 1 -eq 2 ']'
+ for i in 1 2 3 4
+ '[' 2 -eq 2 ']'
+ '[' 2 -eq 2 ']'
+ return 1
+ for i in 1 2 3 4
+ '[' 3 -eq 2 ']'
+ for i in 1 2 3 4
+ '[' 4 -eq 2 ']' <- here is $i == 4
+ return 1
+ for i in 1 2 3 4
+ '[' 3 -eq 2 ']' <- here is $i == 3, correctly behaving as local variable ...
+ for i in 1 2 3 4
+ '[' 4 -eq 2 ']'
+ return 1
Can anyone please tell me, how the for loop works internally? I am bit confused about the for loop list, that is behaving like "local variable".
Thank you very much for all your answers! :)

Shells have no concepts of loop control variables. All variables are global unless their declared function-local, usually with the local keyword.
Some shells have dynamic scoping, which means a callee may access a local variable in its caller if it doesn't overshadow it with its own local variable.
In your example, all this is irrelevant, though. The for statements only write to the variable. They don't read it. When a loop returns, the caller's for continues where it left off.

Every for…in statement has an implicit local state (which I wouldn't call a variable because it has no name and there is no way to inspect it). This local state consists of the unconsumed part of the fully evaluated list of words to iterate over.

Related

How to code the program in the Shell Script [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 2 years ago.
Improve this question
How to code the program in the Shell Script to calculate the sum of the Fibonacci series with the upper limit input from the keyboard? Input for example: "Enter the limit of the Fibonacci series? 10". and the result: 0 + 1 + 1 + 2 + 3 + 5 + 8 + 13 + 21 + 34 = 88
#!/bin/bash
if [ $# -eq 1 ]
then
Num=$1
else
echo -n "Enter the limits of the Fibonacci sequence : "
read Num
fi
a=0
b=1
echo "Result : "
for (( i=0;i<=Num;i++ ))
do
echo -n “$a + “
n=$((a+b))
a=$b
b=$n
done
echo
Anyone know how to get the results?
my expected output is:
Enter the limits of the Fibonacci sequence : 10
Result : 0 + 1 + 1 + 2 + 3 + 5 + 8 + 13 + 21 + 34 = 88
I'm stuck trying
$ cat fib
#!/bin/bash
Num=${1-5}
a=0
b=1
unset s
printf "Result: "
for (( i=0; i<Num; i++ )); do
printf "${s:+ + }$a"
s=$(($s + $a))
n=$((a+$b))
a=$b
b=$n
done
echo " = $s"
$ ./fib 5
Result: 0 + 1 + 1 + 2 + 3 = 7
$ ./fib 10
Result: 0 + 1 + 1 + 2 + 3 + 5 + 8 + 13 + 21 + 34 = 88
This works:
a=1;
b=0;
sum=$a;
for ((i=1;i<10-1;++i)); do
((tmp=b));
((b=a));
((a+=tmp));
((sum+=a));
done;
echo $sum;
Output from my terminal:
[ichramm#hypernova][Wed, 28 Oct 16:19:24][~]
$ a=1; b=0; sum=$a; for ((i=1;i<10-1;++i)); do ((tmp=b)); ((b=a)); ((a+=tmp)); ((sum+=a)); done; echo $sum;
88

"Attempted assignment to a non-variable" in bash

I'm new to Bash and I've been having issues with creating a script. What this script does is take numbers and add them to a total. However, I can't get total to work.It constantly claims that total is a non-variable despite it being assigned earlier in the program.
error message (8 is an example number being entered)
./adder: line 16: 0 = 0 + 8: attempted assignment to non-variable (error token is "= 0 + 8")
#!/bin/bash
clear
total=0
count=0
while [[ $choice != 0 ]]; do
echo Please enter a number or 0 to quit
read choice
if [[ $choice != 0 ]];
then
$(($total = $total + $choice))
$(($count = $count + 1))
echo Total is $total
echo
echo Total is derived from $count numbers
fi
done
exit 0
Get rid of some of the dollar signs in front of the variable names. They're optional inside of an arithmetic context, which is what ((...)) is. On the left-hand side of an assignment they're not just optional, they're forbidden, because = needs the variable name on the left rather than its value.
Also $((...)) should be plain ((...)) without the leading dollar sign. The dollar sign will capture the result of the expression and try to run it as a command. It'll try to run a command named 0 or 5 or whatever the computed value is.
You can write:
((total = $total + $choice))
((count = $count + 1))
or:
((total = total + choice))
((count = count + 1))
or even:
((total += choice))
((count += 1))

Shell for loop, stopping at declaration

I'm trying to write a for loop that goes from 1 to 10, then calculates ( 1 through 10 mod 5) + 2. After that I want to display it like this (1 to 10 mod 5) + 2 = answer. However i'm getting an error at the beginning of the loop which is a syntax error.
for (( i = 0; i <= 10; i++)); do
calculate=(i % 5) + 2
echo ("("i "% 5) + 2" calculate)
done
Try these changes:
calculate=$(( i % 5 + 2 ))
# $(( ... )) is the shell's way to do arithmetic
echo "($i % 5) + 2 = " $calculate
# $x is a way to refer to the value of variable x
# (also inside a double-quoted string)
The for loop header is actually OK.

Bash (Mint): How to add a newline to a PS3 prompt?

I'm trying this stupid code
PS3="Choose 1..10\n0 to exit";
But bash shows me literally \n, and not a new line.
I'm trying to use it in a bash script with select a 'builtin' choice prompt.
$ PS3="Choose 1..10"$'\n'"0 to exit: "
$ select choice in $(seq 10)
> do
> [[ $choice -eq 0 ]] && break
> #do stuff for choice 1 to 10
> done
1) 1
2) 2
3) 3
4) 4
5) 5
6) 6
7) 7
8) 8
9) 9
10) 10
Choose 1..10
0 to exit: 0
$
Just an additional $'\n' is required wrt bash context, which is inserted in between the PS3 environment variable. It's similar to the way we set it for IFS
You can also embed a newline in a string directly:
PS3="Choose 1..10
0 to exit: "

How to fork a function with params in bash?

I'm new to bash scripting and I faced an issue when I tried to improve my script. My script is spliting a text file and each part of this text file is processed in a function ... Everything is working fine but my problem occurs when I'm forking (with &) my function processing ! My args are not like expected (it's a line number and text with whitespaces and backspaces) and I suppose it's because of global variables ... I tried to fork, then sleep 1 second in the parent thread and then continue in order to put args into local variables for my function execution but it doesn't work either ... Can you give me a hint about how to do it ? What I want is to be able to pass args to my function and be allowed to modify it after the fork call in my parent thread ... is it possible ?
Thanks in advance :)
Here is my script :
#!/bin/bash
#Parameters#
FILE='tasks'
LINE_BY_THREAD='500'
#Function definition
function checkPart() {
local NUMBER="$1"
local TXT="$2"
echo "$TXT" | { while IFS= read -r line ; do
IFS=' ' read -ra ADDR <<< "$line"
#If the countdown is set to 0, launch the task ans set it to init value
if [ ${ADDR[0]} == '0' ]; then
#task launching
#to replace by $()l
echo `./${ADDR[1]}.sh ${ADDR[2]} &`
#countdown set to init value
sed -i "$NUMBER c ${ADDR[3]} ${ADDR[1]} ${ADDR[2]} ${ADDR[3]}" $FILE
else
sed -i "$NUMBER c $((ADDR-1)) ${ADDR[1]} ${ADDR[2]} ${ADDR[3]}" $FILE
fi
((NUMBER++))
done }
}
#Init processes number#
LINE_NUMBER=$(wc -l < $FILE)
NB_PROCESSES=$(($LINE_NUMBER / $LINE_BY_THREAD))
if [ $(($LINE_NUMBER % $LINE_BY_THREAD)) -ne '0' ]; then
((NB_PROCESSES++))
fi
echo "Number of thread to be run : $NB_PROCESSES"
#Start the split sequence#
for (( i = 2; i <= $LINE_NUMBER; i += $LINE_BY_THREAD ))
do
PARAM=$(sed "$i,$(($i + $LINE_BY_THREAD - 1))!d" "$FILE")
(checkPart "$i" "$PARAM") &
sleep 1
done
My job is to create a scheduler for tasks described in this following file :
#MinutesBeforeLaunch#TypeOfProcess#Argument#Frequency#
2 agr_m 42 5
5 agr_m_s 26 5
0 agr_m 42 5
3 agr_m_s 26 5
0 agr_m 42 5
5 agr_m_s 26 5
4 agr_m 42 5
5 agr_m_s 26 5
4 agr_m 42 5
4 agr_m_s 26 5
2 agr_m 42 5
4 agr_m_s 26 5
When I'm reading a number > 0 in the first column, I just decrement it and when it's a 0 I have to launch the task and set the first number to frequency, last column ...
My first code is the previous with sed for text replacement but is it possible to do better ?

Resources