1

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! :)

closed as unclear what you're asking by l0b0, Stephen Rauch, GAD3R, Rui F Ribeiro, Anthon Jun 25 '17 at 19:02

Please clarify your specific problem or add additional details to highlight exactly what you need. As it's currently written, it’s hard to tell exactly what you're asking. See the How to Ask page for help clarifying this question. If this question can be reworded to fit the rules in the help center, please edit the question.

1
$ reset() { i=$1; }
$ for i in 1 2 3 4 ; do echo -n "$i "; reset 3; echo "$i" ; done;
1 3
2 3
3 3
4 3

i is not made local in reset, so the change there is visible outside the function. But the change to i does not affect the values i gets on the following iterations. The words listed in the for command are assigned to i one at a time, in order, as expected.

So yes, there is a "hidden pointer" that tells the position in the word list. There has to be, otherwise loops with repeating words, like for i in a a a ; do ... would not work. There i gets the same value on each iteration, but the loop still runs a fixed number of iterations.

  • Thank you very much, I like your example! I accepted your answer :) – Bomba Jun 24 '17 at 18:56
1

I'm not sure what you're trying to do, but

  1. You never return 0 ("true") from the function, so it will never return until it's tried every combination.
  2. $i is not a local variable:

    $ foo() { i=1; bar; }
    $ bar() { echo $i; }
    $ foo
    1
    
  3. You might want to debug this by seeing which line each command is on:

    trap 'printf "$LINENO "' DEBUG
    
  4. $1 is local to each function call, and does not carry over even when a function calls itself:

    $ foo() { if [ $# -ne 0 ]; then echo "One more time"; foo; fi; }
    $ foo 1
    One more time
    

    This is the case in every major high-level programming language. Otherwise, recursion would be completely broken.

  • Thanks for your answer. 1. Yes, I know. I did it on purpose so I can see the $i value after the called function returns 2. Yes, i is a global variable, but how does then for loop know where to continue its iteration after all the recursive functions calls? Is there some "hidden pointer" to the last value that was used in the for loop that is uniqe to each for loop? Thanks a lot. – Bomba Jun 24 '17 at 18:02
  • Thank you very much for the edit. This is very good answer, I wish I could accept more answers, but I think that @ilkkachu post answers my questions. Also, thanks for the tip about debug, did not know it. – Bomba Jun 24 '17 at 18:54

Not the answer you're looking for? Browse other questions tagged or ask your own question.