what is difference between (loopcount=loopcount+1) and ((loopcount=loopcount+1))? - linux

loopcount=1
loopmax=5
while [ $loopcount -le $loopmax ]
do
echo "loop iteration :$loopcount"
((loopcount=loopcount+1))
done
for this i am getting o/p like this
loop iteration :1
loop iteration :2
loop iteration :3
loop iteration :4
loop iteration :5
but if i change program ((loopcount=loopcount+1)) to (loopcount=loopcount+1) i am getting bellow output.
loop iteration :1
loop iteration :1
loop iteration :1
loop iteration :1
loop iteration :1
loop iteration :1
getting infinite times . what is difference of () and (()) ?

(...) means to run the given command in a subshell. ((...)) means to do the arithmetic operation within the parens.
Note that a subshell cannot change the variables of the parent shell, so in your example you never update the value of loopcount in the parent. Also, in your single paren example, you would not be doing arithmetic, you'd be assigning the string loopcount+1 to the variable loopcount so that if you did printf "%s\n" "$loopcount" after that you would get the output loopcount+1

From man bash:
(list) list is executed in a subshell environment (see COMMAND EXECUTION ENVIRONMENT below). Variable assignments and builtin commands that affect the shell's environment do not remain in effect after the command completes. The return status is the exit status of list.
[...]
((expression)) The expression is evaluated according to the rules described below under ARITHMETIC EVALUATION. If the value of the expression is non-zero, the return status is 0; otherwise the return status is 1. This is exactly equivalent to let "expression".

Related

Stopping an infinite loop and breaking at end of loop

I'm running an infinite while loop in bash that has a counting element to it.
while :
do
#stuff including counting
trap break int
done
This works as I would like for the most part. When I hit Ctrl+C, the loop stops, but the script continues. However, the loop stops in the middle of the loop which means that the final count is inaccurate.
Is there a way to make this loop break at the very end?
Instead of executing break you can set a flag and break the loop at a designated location when the flag is set. Note that in a loop the loop head can also be seen as the very end of the loop.
state=run
trap 'state=end' int
while [ "$state" = run ]
do
# stuff
done
If you want to break somewhere in the middle use
state=run
trap 'state=end' int
while :
do
# stuff
[ "$state" = end ] && break
# more stuff
done
You can test the behavior quiet nicely by replacing # stuff with for i in {1..20}; do printf .; sleep 0.1; done; echo:
As we can see in the clip above, the outer loop finishes its current iteration (# stuff) until the very end.

Conditional statement not working as expected

I am using Konsole on kubuntu 14.04.
I want to take arguments to this shell-script, and pass it to a command. The code is basically an infinite loop, and I want one of the arguments to the inner command to be increased once every 3 iterations of the loop. Ignoring the actual details, here's a gist of my code:
#!/bin/bash
ct=0
begin=$1
while :
do
echo "give: $begin as argument to the command"
#actual command
ct=$((ct+1))
if [ $ct%3==0 ]; then
begin=$(($begin+1))
fi
done
I am expecting the begin variable to be increased every 3 iterations, but it is increasing in the every iteration of the loop. What am I doing wrong?
You want to test with
if [ $(expr $cr % 3) = 0 ]; then ...
because this
[ $ct%3==0 ]
tests whether the string $ct%3==0, after parameter substitution, is not empty. A good way for understanding this is reading the manual for test and look at the semantics when it is given 1, 2, 3 or more arguments. In your original script, it only sees one argument, in mine it sees three. White space is very important in the shell. :-)
In BASH you can completely utilize ((...)) and refactor your script like this:
#!/bin/bash
ct=0
begin="$1"
while :
do
echo "give: $begin as argument to the command"
#actual command
(( ct++ % 3 == 0)) && (( begin++ ))
done

Whiptail Gauge: Variable in loop not being set

Am new to bash and whiptail so excuse the ignorance.
When assigning a var in the for loop, the new value of 20 is never set when using a Whiptail dialog. Any suggestions why ?
andy="10"
{
for ((i = 0 ; i <= 100 ; i+=50)); do
andy="20"
echo $i
sleep 1
done
} | whiptail --gauge "Please wait" 5 50 0
# }
echo "My val $andy
A command inside a pipeline (that is, a series of commands separated by |) is always executed in a subshell, which means that each command has its own variable environment. The same is true of the commands inside the compound command (…), but not the compound command {…}, which can normally be used for grouping without creating a subshell.
In bash or zsh, you can solve this problem using process substitution instead of a pipeline. For example:
andy="10"
for ((i=0 ; i <= 100 ; i+=50)); do
andy="20"
echo $i
sleep 1
done > >(whiptail --gauge "Please wait" 6 50 0)
echo "My val $andy
>(whiptail ...) will cause a subshell to be created to execute whiptail; the entire expression will be substituted by the name of this subshell's standard input (in linux, it will be something like /dev/fd/63, but it could be a FIFO on other OSs). > >(...) causes standard output to be redirected to the subshell's standard input; the first > is just a normal stdout redirect.
The statements inside {} are not ordinarily executed in a sub-shell. However, when you add a pipe (|) to it, they seem to be executed in a sub-shell.
If you remove the pipe to whiptail, you will see the update value of andy.

Syntax error: operand expected when using Bash

I have two arrays that I want to loop in. I construct those properly and before going into for loop, I do echo them to be sure everything is ok with arrays.
But when I run the script, it outputs an error:
l<=: syntax error: operand expected (error token is "<="
I consulted the mighty Google and I understood it suffers from the lack of the second variable, but I mentioned earlier I do echo the values and everything seems to be OK. Here is the snippet..
#!/bin/bash
k=0
#this loop is just for being sure array is loaded
while [[ $k -le ${#hitEnd[#]} ]]
do
echo "hitEnd is: ${hitEnd[k]} and hitStart is: ${hitStart[k]}"
# here outputs the values correct
k=$((k+1))
done
k=0
for ((l=${hitStart[k]};l<=${hitEnd[k]};l++)) ; do //this is error line..
let array[l]++
k=$((k+1))
done
The variables in the for loop are echoed correctly but for loop won't work.. where am I wrong?
#
as gniourf_gniourf answered:
"... At some point, k will reach the value ${#hitEnd[#]}, and this is
exactly when hitEnd[k] is not defined and expands to an empty string!
Bang!"
meaning error output is displayed not at the beginning of the loop, but when k has a greater value than array's indices, pointing an index that array does not include...
That's because at some point ${hitEnd[k]} expands to nothing (it is undefined). I get the same error with ((l<=)). You should write your for loop as:
k=0
for ((l=${hitStart[0]};k<${#hitEnd[#]} && l<=${hitEnd[k]};l++)); do
so as to always have an index k that corresponds to a defined field in the array ${hitEnd[#]}.
Also, instead of
k=$((k+1))
you can just write
((++k))
Done!
Your script revised using better modern bash practice:
#!/bin/bash
k=0
#this loop is just for being sure array is loaded
while ((k<=${#hitEnd[#]})); do
echo "hitEnd is: ${hitEnd[k]} and hitStart is: ${hitStart[k]}"
# here outputs the values correct
((++k))
done
k=0
for ((l=hitStart[0];k<${#hitEnd[#]} && l<=hitEnd[k];++l)); do
((++array[l]))
((++k))
done
Now, I'm not too sure the for loop does exactly what you want it to... Don't you mean this instead?
#!/bin/bash
# define arrays hitStart[#] and hitEnd[#]...
# define array array[#]
#this loop is just for being sure array is loaded
for ((k=0;k<${#hitEnd[#]};++k)); do
echo "hitEnd is: ${hitEnd[k]} and hitStart is: ${hitStart[k]}"
# here outputs the values correct
((++k))
done
for ((k=0;k<${#hitEnd[#]};++k)); do
for ((l=hitStart[k];l<=hitEnd[k];++l)); do
((++array[l]))
done
done
A bit bandaid-y, but you rewrite your for-loop into a while loop:
l="${hitStart[k]}"
while [[ "$l" -le "${hitEnd[k]}" ]]; do
let array[l]++
k=$((k+1))
l=$((l+1))
done

Empty Body For Loop Linux Shell

Hi I want to write and empty body loop. I just want the loop counter to increment so I want the cpu to stay busy without any IO operation. Here is what I have written but it gives me an error:
#!/bin/bash
for (( i = 0 ; i <= 1000000; i++ ))
do
done
root#ubuntu:~# ./forLoop
./forLoop: line 4: syntax error near unexpected token `done'
./forLoop: line 4: `done'
You must specify at least one command in a loop body.
The best command for such a purposes is a colon :, commonly used as a no-op shell command.
You could put a no op command inside the loop like true or false (do nothing successfully or unsuccessfully respectively).
This will be a tight loop and will burn CPU. Unless you want to warm up your computer on a cold morning, you can simply say i=1000000 and have the same effect as the loop.
What is it that you're trying to achieve?
#!/bin/bash
let i=0
while [[ $i -le 1000000 ]]; do
let i++
done
You could use sleep x if you want to delay for x number of seconds.

Resources