I do not understand the break command - linux

in:
#!/bin/sh
for var1 in 1 2 3
do
for var2 in 0 5
do
if [ $var1 -eq 2 -a $var2 -eq 0 ]
then
break 2
else
echo "$var1 $var2"
fi
done
done
the output is:
1 0
1 5
and then script stops.
how ever if the break command's argument (2) is removed, the output is:
1 0
1 5
3 0
3 5
What i am asking is why 3 0 and 3 5 are printed, when the script is conditioned not to break? script didn't print 2 0 and 2 5, and 3 0 and 3 5 should signal a break as well...

The optional argument to break tells it which loop to break out of. If the argument is omitted, it breaks out of the innermost loop. With an argument n it breaks out of the nth enclosing loop.
So break 2 breaks out of the for var1 loop, because it's the 2nd enclosing loop. If you change it to break, it just breaks out of the for var2 loop, so it goes to the next iteration of for var1.

To summarize the comments, there were two issues:
Why is 3 0 printed after a break, but not after break 2?
This was because the condition ([ $var1 -eq 2 -a $var2 -eq 0 ]) checked for equality rather than -ge, greater or equal. With -ge there will be no echos where both numbers are greater.
The break 2 instead exited both loops, thereby giving the same effect in this particular case. If the loop had been for var1 in 1 2 0, break 2 would have also prevented 0 0 from showing up since both loops would have been stopped.
Why is 2 5 not printed after a brake?
This is because the entire inner loop stops on a break, so no other iterations will have their chance to echo. To instead skip the current iteration and immediately try the next one, use continue.

Just a simple break breaks out of one loop - the inner for loop in your case.
However, if you use an additional integer in the break statement, as in break 2, then breaks out of the specified number of loops - that is two for loops in your case. Since there are no more than two nested loops and there is no more code after the outermost loop, it is effectively the same as ending the script.

Related

TCL "join" command not merging list into single string

With the "join" command I've assumed tcl would merge a list of elements into a single string with delimiter.
However this is not what I see at my terminal. Also, without a delimiter it returns the same list of elements with a space in between although ideally it should merge them with no spaces
Example:
## Setting original string
set A [list 1 2 3]
% 1 2 3
puts [llength $A]
% 3
## Join list without delimiter
set B [join $A]
% 1 2 3
puts [llength $B]
% 3
## Join list with space delimiter (actual requirement)
set C [join $A " "]
% 1 2 3
puts [llength $C]
% 3
## Join list with comma delimiter (to also visibly check what happens to each element of list)
set D [join $A ","]
% 1, 2, 3
puts [llength $D]
% 3
foreach item $D {puts $item}
1,
2,
3
Not sure what is going wrong here.
I am trying to set a variable as a single string "1 2 3"
Trying to merge all elements of a list into single string.
However "join" returns the same list as initial but with delimiter added to each element of list (except last).
EDIT: On my new machine, the [join $A ","] is working correctly as 1,2,3 without spaces.
The highly unlikely bit is this:
set A [list 1 2 3]
# ==> 1 2 3
set D [join $A ","]
# ==> 1, 2, 3
as when I put that into a fresh Tcl session I instead get a final output of 1,2,3 (and that's not behaviour anyone's planning to change). I'm guessing you have a stray space in there or have defined a custom version of join.

Print out even numbers between two given integers

How would I print out even numbers between two numbers?
I have a script where a user enters in two values and them two values are placed into their respective array elements. How would I print the even numbers between the two values?
See man seq. You can use
seq first incr last
for example
seq 4 2 18
to print even numbers from 4 to 18 (inclusive)
If you have bash.
printf '%s\n' {4..18..2}
Or a c-style for loop
for
for ((i=4;i<=18;i+=2)); do echo "$i"; done

awk key-value issue for array

I meet a awk array issue, details as below:
[~/temp]$ cat test.txt
1
2
3
4
1
2
3
Then I want to count the frequency of the number.
[~/temp]$ awk 'num[$1]++;END{for (i in num){printf("%s\t%-s\n", num[i],i)|"sort -r -n -k1"} }' test.txt
1
2
3
2 3
2 2
2 1
1 4
As you see, why does the output of first 3 line '1 2 3' will come blank value?
Thank for your answer.
An awk statement consists of a pattern and related action. Omitted pattern matches every record of input. Omitted action is an alias to {print $0}, ie. output the current record, which is what you are getting. Looking at the first part of your program:
$ awk 'num[$1]++' file
1
2
3
Let's change that a bit to understand what happens there:
$ awk '{print "NR:",NR,"num["$1"]++:",num[$1]++}' file
NR: 1 num[1]++: 0
NR: 2 num[2]++: 0
NR: 3 num[3]++: 0
NR: 4 num[4]++: 0
NR: 5 num[1]++: 1
NR: 6 num[2]++: 1
NR: 7 num[3]++: 1
Since you are using postfix operator num[$1]++ in the pattern, on records 1-4 it gets evaluated to 0 before it's value is incremented. Output would be different if you used the prefix operator ++num[$1] which would first increment the value of the variable after which it would get evaluated and would lead to outputing every record of input, not just the last three, which you were getting.
Correct way would've been to use num[$1]++ as an action, not as a pattern:
$ awk '{num[$1]++}' file
Put your "per line" part in {} i.e. { num[$1]++; }
awk programs a a collection of [pattern] { actions } (the pattern is optional, the {} is not). Seems that in your case your line is being treated as the pattern.

Creating a nested for loop in bash script

I am trying to create a nested for loop which will count from 1 to 10 and the second or nested loop will count from 1 to 5.
for ((i=1;i<11;i++));
do
for ((a=1;a<6;a++));
do
echo $i:$a
done
done
What I though the output of this loop was going to be was:
1:1
2:2
3:3
4:4
5:5
6:1
7:2
8:3
9:4
10:5
But instead the output was
1:1
1:2
1:3
1:4
1:5
2:1
...
2:5
3:1
...
and same thing till 10:5
How can I modify my loop to get the result I want!
Thanks
Your logic is wrong. You don't want a nested loop at all. Try this:
for ((i=1;i<11;++i)); do
echo "$i:$((i-5*((i-1)/5)))"
done
This uses integer division to subtract the right number of multiples of 5 from the value of $i:
when $i is between 1 and 5, (i-1)/5 is 0, so nothing is subtracted
when $i is between 6 and 10, (i-1)/5 is 1, so 5 is subtracted
etc.
You must not use a nested loop in this case. What you have is a second co-variable, i.e. something that increments similar to the outer loop variable. It's not at all independent of the outer loop variable.
That means you can calculate the value of a from i:
for ((i=1;i<11;i++)); do
((a=((i-1)%5)+1))
echo $i:$a
done
%5 will make sure that the value is always between 0 and 4. That means we need i to run from 0 to 9 which gives us i-1. Afterwards, we need to move 0...4 to 0...5 with +1.
I know #AaronDigulla's answer is what OP wants. this is another way to get the output :)
paste -d':' <(seq 10) <(seq 5;seq 5)
a=0
for ((i=1;i<11;i++))
do
a=$((a%5))
((a++))
echo $i:$a
done
If you really need it to use 2 loops, try
for ((i=1;i<3;i++))
do
for ((a=1;a<6;a++))
do
echo "$((i*a)):$a"
done
done

How to add more than 1 number in a loop in Bash script?

So I have this loop:
for ((i=100;i<1001;i++));
do
echo $i
sleep 1
done
I want to know how I can add 10 every time the loop repeats instead of adding 1. So I want the loop to look something like this:
110
120
130
140
and keep going until it reaches the limit or stopped!
Thanks
for ((i=100;i<1001;i+=10));
do
echo $i
sleep 1
done
Simply change ++ to += 10

Resources