While command

I would like to hold up with the syntax of while below i follow the instruction of the command but I'm wondering it isn't working !!
can any one please explain where is the error

* #!/bin/bash
* n=1
* while [ $n <= 5 ]
* do echo Welcome $n times
* n=$((n+1))
* $n
* done

It's
while [ $n -le 5 ]
(less or equal).

The [ is a command, and the < is a redirection.
Compare with
while [[ $n <= 5 ]]
this works (is not a command) but does an alphabetic comparison; better is again
while [[ $n -le 5 ]]
Also possible, a comparison in a numeric context:
while (( $n <= 5 )) or
while (( n <= 5 ))

3 Likes

The idiomatic way to do "countable" loops is usually:

for n in {1..5}; do
    echo Welcome $n times
done

The while construct is more usual when you want the shell to wait for an event, such as a file being created or a timer expiring, or reading data from a source of unknown length.

3 Likes

@Abdullah ,

check out the official bash documentation wrt looping - link below should take you to relevant section

1 Like

The {1..5} is even for "fixed countables". You cannot do {1..$nmax}
But the following works:

nmax=5
for (( n=1; n <= nmax; n+=1 ))
do
  "echo Welcome $n times"
done
2 Likes

That can be fixed with eval (but not very safely). I prefer:

$ b=6; s=7; e=50;
$ for j in $( seq $b $s $e ); do echo $j; done
6
13
20
27
34
41
48

I'm guessing that {1..1000000} is done by a shell generator function, but seq makes a long arg list: I never hit a limit on this, though. Both methods run in about 40 secs (11 seconds without the echo), and produce identical output.

I'm curious, why is it "not very safely"? (in context of working only with properly formatted numbers)
I constantly keep seeing people repeating this like a vastly misunderstood mantra, but when it comes to presenting some evidence of a "life-threatening danger" of it, they just "meh, nevermind - it's dangerous in general, stop bothering me" :slight_smile:

So, you have a variable var with a defined structure ([0-9] digits only), you usually do sanitation (if you're savy enough) before passing such variable to eval: var=${var//[!0-9]/} && eval echo {1..$var}, because if it didn't contain only digits, then it wouldn't make sense to use brace expansion on it.
So what is so very not safe about it? What's the worst that can happen, apart from shell/script exiting due to "unable to allocate memory" error?
(if this is the ultimate danger, you either accept it, or can adress it further at sanitation stage, e.g. "trim it down"; exit immediately, if it's above the integer limit - see below, or if it's above other, arbitrarily set limit)

In Bash it would likely be 9223372036854775807 (or 0x7FFFFFFFFFFFFFFF), but yeah in seq it's... much, much higher :smiley: (gave up testing after quickly reaching 2^1024->2^1024+1)

Btw, printf "%d\n" {6..50..7} :wink:

PS. If you don't intend to do a proper sanitation - then yes, don't use eval, or it may f**k up your life badly, when someone passes a destructive command stored in a variable.

I don't recommend eval because it is an attack vector through injected code. If somebody else uses it for a different purpose than how I used it, they may not realise there is a risk in their usage.

It seems to me that $( seq 1 1000000 ) would get evaluated in full and make a rather long string. I don't think it would be run in parallel to the loop, like a pipe would. The {1..1000000} could be run in parallel if the shell was smart enough to deal with it through a subprocess, but I don't know whether it is implemented that way.

I'm well aware that {6..50..7} is available for step counts, but it still does not work with variables. I just threw that in to expand the example. Slightly annoying that seq takes the values in a different order.

@Paul_Pedant , there's alway the roll your own when in control of the environment.

x=34; for step in $(eval echo {1..$x..7});do echo $step;done
1
8
15
22
29

Thanks for ur advise i have edited the file but the file still don't working, this command could give me an infinite loop....
but the following comment appears;
./Testwhile: line 3: [: 1: unary operator expected

n=1
while [ $n >= 5 ] #n should have an intial value
do echo Welcome $n time
n=$(( n+1))
$n
done
shellcheck -s bash abdullah.sh 

In abdullah.sh line 2:
while [ $n >= 5 ] #n should have an intial value
           ^-- SC2071 (error): >= is not a valid operator. Use -ge .

For more information:
  https://www.shellcheck.net/wiki/SC2071 -- >= is not a valid operator. Use -...

additionally on line 5 $n means nothing so needs to be removed

also starting the loop where n=1 and testing -ge 5 is never going to execute the while loop as n == 1 therefore is < 5

finally, the example given by teammate @MadeInGermany works perfectly well (for me at least)

perhaps you were after something along the following

n=1
while [ $n -le 5 ] #n should have an intial value
do
	echo Welcome $n time
	n=$(( n+1))
done
1 Like