Nested For loop counter reset - bash

I'm trying to run nested for loops that use lists as their loop counters. The problem is that once the 'delta' loop reaches 100, it doesnt reset to 0. Same problem for 'edges'.
I tried this, but it doesnt seem to work for my loops.
http://tldp.org/LDP/abs/html/nestedloops.html
Any ideas here? Here's my code:
#!/bin/sh
threads="1 2 4 8 16 32 64 96 128 160 192 224 256"
delta="0 10 20 30 40 50 60 70 80 90 100"
edges="16 8192"
nodes="16384"
for threads in $threads
do
for delta in $delta
do
for edges in $edges
do
for nodes in $nodes
do
printf "\n"
echo $threads
echo $delta
echo $edges
echo $nodes
done
done
done
done
expected output:
1 0 16 16384
1 0 8192 16384
1 10 16 16384
1 10 8192 16384
1 20 16 16384
1 20 8192 16384

When using for loops like this, make sure you give the loop variable a different name than the variable you are iterating over.
Using for threads in $threads makes it confusing to distinguish between the looping variable (threads) and the thing you're looping over ($threads).
When you call echo $threads later, bash doesn't know you're referring to the first one.
In this case, you could change the loop declarations to something like for n in nodes or for t in threads, then echo out $n and $t inside the innermost loop to obtain the desired output.

Related

How do I create a bash script, using loops, to create a Multiplication Table with 5 column/10 row format

Here is what I have:
#!/bin/bash
#create a multiplication table 5 columns 10 rows
echo " Multiplication Table "
echo "-----+-------------------------"
for x in {0..5}
do
for y in {0..10}
do
echo -n "$(( $x * $y )) "
done
echo
echo "-----+--------------------------"
done
This is my Output:
Multiplication Table
-----+-------------------------
0 0 0 0 0 0 0 0 0 0 0
-----+--------------------------
0 1 2 3 4 5 6 7 8 9 10
-----+--------------------------
0 2 4 6 8 10 12 14 16 18 20
-----+--------------------------
0 3 6 9 12 15 18 21 24 27 30
-----+--------------------------
0 4 8 12 16 20 24 28 32 36 40
-----+--------------------------
0 5 10 15 20 25 30 35 40 45 50
-----+--------------------------
This is the Needed Output:
Multiplication Table
----+-------------------------------------
| 0 1 2 3 4
----+-------------------------------------
0 | 0 0 0 0 0
1 | 0 1 2 3 4
2 | 0 2 4 6 8
3 | 0 3 6 9 12
4 | 0 4 8 12 16
5 | 0 5 10 15 20
6 | 0 6 12 18 24
7 | 0 7 14 21 28
8 | 0 8 16 24 32
9 | 0 9 18 27 36
----+-------------------------------------
I've tried to write this many different ways, but I'm struggling with finding a way to format it correctly. The first is pretty close, but I need it to have the sequential numbers being multiplied on the top and left side. I'm not sure how to use, or if I can use, the seq command to achieve this or if there is a better way. I also need to have straight columns and rows with the defining lines setting the table layout, but my looking up the column command hasn't produced the right output.
Here was my final output and code:
#!/bin/bash
#create a multiplication table 5 columns 10 rows
#Create top of the table
echo " Multiplication Table"
echo "----+------------------------------"
#Print the nums at top of table and format dashes
echo -n " |"; printf '\t%d' {0..5}; echo
echo "----+------------------------------"
#for loops to create table nums
for y in {0..9}
do
#Print the side nums and |
echo -n "$y |"
#for loop to create x
for x in {0..5}
do
#Multiply vars, tab for spacing
echo -en "\t$((x*y))"
done
#Print
echo
done
#Print bottom dashes for format
echo "----+------------------------------"
I changed a bit of Armali's code just to make it more appealing to the eye, and the echo was moved to the bottom (out of the loop) so it didn't print as many lines. But again, thank you Armali, as I would've spent a lot more time figuring out exactly how to write that printf code to get the format correct.
I'm not sure how to use, or if I can use, the seq command to achieve this …
seq offers no advantage here over bash's sequence expression combined with printf.
This variant of your script produces (with the usual 8-column tabs) the needed output:
#!/bin/bash
#create a multiplication table 5 columns 10 rows
echo " Multiplication Table"
echo "----+-------------------------------------"
echo -n " |"; printf '\t%d' {0..4}; echo
echo "----+-------------------------------------"
for y in {0..9}
do echo -n "$y |"
for x in {0..4}
do echo -en "\t$((x*y))"
done
echo
echo "----+-------------------------------------"
done

Referencing one number to another number

I have a question which I think is fairly simply but I am new to Bash and can't find much info on this.
5 references 3
10 references 4
20 references 10
30 references 20
inputBeforeLookup = 5 #this the number which needs to look up 3 above^^^^
# 10 would lookup and return 4
#20 returns 10
start = 1
end = $start + $lookupNumberfromFile # 3 in this case, since input was 5
seq $start $end
1
2
3
4
I guess my question here is what is the proper way to create like a configuration file which references numbers to other numbers?
If there is a better way than the snippet of code I posted I am always open to suggestions, like i said I am learning.
I am new to this so I am not sure if the syntax is 100% correct. I am more so looking for a solution on the best way to solve the problem.
Hope this sample helps you regarding variable expansion in bash:
Notice that: the \ prevents the expansion of $$ (current process id). For triple substitution you need double eval and so on....
#!/bin/bash
one=1
two=one
three=two
four=three
five=four
echo $one
eval echo \$$two
eval eval echo \\$\$$three
eval eval eval echo \\\\$\\$\$$four
eval eval eval eval echo \\\\\\\\$\\\\$\\$\$$five
Output:
1
1
1
1
1
Bonus:
In zsh you can use nested substitution much more easily:
#!/bin/zsh
one=1
two=one
three=two
four=three
five=four
echo $one
echo ${(P)two}
echo ${(P)${(P)three}}
...
http://zsh.sourceforge.net/Doc/Release/Expansion.html
Set up an associative array, then test it with numbers 1 to 30. Those numbers that don't reference other numbers are printed as is:
MYMAP=( [5]=3 [10]=4 [20]=10 [30]=20 )
seq 30 | while read x ; do echo ${MYMAP[$x]:-$x} ; done | paste - - - - -
That last | paste - - - - - isn't necessary, but 5 column output is easier to follow given that the input has several multiples of 5. Output:
1 2 3 4 3
6 7 8 9 4
11 12 13 14 15
16 17 18 19 10
21 22 23 24 25
26 27 28 29 20

Generating random numbers that conform to a range in Bash

If I run ./random.sh 10 45, it would only return random numbers between 10 and 45.
I am able to produce the random number using
randomNumber=$((1 + RANDOM % 100))
but now how can I allow user to specify upper and lower limit of random number?
You can use shuf
#!/bin/bash
# $1: Lower limit
# $2: Upper limit
# Todo Check arguments
shuf -i $1-$2 -n 1
./random.sh 45 50
Try the following (pure BASH):
low=10
hgh=45
rand=$((low + RANDOM%(1+hgh-low)))
The idea is to set your range at a default lower bound, say 10, with a higher bound, say 45. So you adjust the lower bound like this : $RANDOM % 45 + 10, don't you?
But there is a problem with this solution, it assumes that you'll always be between 0 + 10 and 45 so in fact it works until you reach 35 (35 + 10 = 45 your higher bound), anymore than 35 will be out of your bounds.
The solution in order to stay in the range is to do $RANDOM % (higher_b - lower_b) which will allow you to stay in higher bound then to add lower bound which gives you :
$RANDOM % (45 -10) + 10
example wrong output:
for i in {0..10};do printf $[RANDOM % 45 + 10]" ";done
47 31 53 23 36 10 22 36 11 25 54
example right output:
for i in {0..10};do printf $[RANDOM % 35 +10]" ";done
39 44 14 12 38 31 25 13 42 33 16
You can also write RANDOM % (higher - lower +1) if you want your index to include higher bound.
The trouble with modulo is that $RANDOM % N, unless N is a power of 2, does not have an equal probability distribution for all results: How to generate random number in Bash?. Per man bash, $RANDOM produces a number between 0 and 32,767 (2**15-1). This may not matter much in some situations, but by rewriting the expression slightly we do get an equal distribution.
for i in {0..10}; do echo -n "$((RANDOM*36/32768 + 10)) "; done; echo
A bash script with a user-selectable range:
#!/bin/bash
LOW=$1
HIGH=$2
echo $((RANDOM * ($HIGH-$LOW+1) / 32768 + LOW))
You will want to do some parameter checking also.

Labelling of text files in bash loop

This script should give values to the program through the command line and store the output of the terminal in their corresponding files. Since I cannot put a decimal-point number as part of the label of the file, I intent to multiply each kappa by 10000 to turn them into integers and use them as labels, but I did it wrong in the code and I don't know how to it properly. How does it work? Thank you!
#!/bin/bash
for kappa in $(seq 0.0001 0.000495 0.01);
do
kappa_10000 = $kappa * 10000;
for seed in {1..50};
do
./two_defects $seed $kappa > "equilibration_step_seed${seed}_kappa${kappa_10000}.txt";
done
done
Bash does not do floating-number calculation as pointed out by #Inian. A program like bc must be called and its output can be directly stored in a variable as follows:
for kappa in $(seq 0.0001 0.000495 0.01)
do
kappa_10000=$(echo "$kappa*10000/1" | bc)
echo kappa_10000
done
The output in the terminal would be
1
5
10
15
20
25
30
35
40
45
50
55
60
65
70
75
80
85
90
95
100
The /1 operation must be added to make the output an integer.

bash 'while read line' efficiency with big file

I was using a while loop to process a task,
which read records from a big file about 10 million lines.
I found that the processing become more and more slower as time goes by.
and I make a simulated script with 1 million lines as blow, which reveal the problem.
but I still don't know why, how does the read command work?
seq 1000000 > seq.dat
while read s;
do
if [ `expr $s % 50000` -eq 0 ];then
echo -n $( expr `date +%s` - $A) ' ';
A=`date +%s`;
fi
done < seq.dat
The terminal outputs the time interval:
98 98 98 98 98 97 98 97 98 101 106 112 121 121 127 132 135 134
at about 50,000 lines,the processing become slower obviously.
Using your code, I saw the same pattern of increasing times (right from the beginning!). If you want faster processing, you should rewrite using shell internal features. Here's my bash version:
tabChar=" " # put a real tab char here, of course
seq 1000000 > seq.dat
while read s;
do
if (( ! ( s % 50000 ) )) ;then
echo $s "${tabChar}" $( expr `date +%s` - $A)
A=$(date +%s);
fi
done < seq.dat
edit
fixed bug, output indicated each line was being processed, now only every 50000'th line gets the timing treatment. Doah!
was
if (( s % 50000 )) ;then
fixed to
if (( ! ( s % 50000 ) )) ;then
output now echo ${.sh.version} = Version JM 93t+ 2010-05-24
50000
100000 1
150000 0
200000 1
250000 0
300000 1
350000 0
400000 1
450000 0
500000 1
550000 0
600000 1
650000 0
700000 1
750000 0
output bash
50000 480
100000 3
150000 2
200000 3
250000 3
300000 2
350000 3
400000 3
450000 2
500000 2
550000 3
600000 2
650000 2
700000 3
750000 3
800000 2
850000 2
900000 3
950000 2
800000 1
850000 0
900000 1
950000 0
1e+06 1
As to why your original test case is taking so long ... not sure. I was surprised to see both the time for each test cyle AND the increase in time. If you really need to understand this, you may need to spend time instrumenting more test stuff. Maybe you'd see something running truss or strace (depending on your base OS).
I hope this helps.
Read is a comparatively slow process, as the author of "Learning the Korn Shell" points out*. (Just above Section 7.2.2.1.) There are other programs, such as awk or sed that have been highly optimized to do what is essentially the same thing: read from a file one line at a time and perform some operations using that input.
Not to mention, that you're calling an external process every time you're doing subtraction or taking the modulus, which can get expensive. awk has both of those functionalities built in.
As the following test points out, awk is quite a bit faster:
#!/usr/bin/env bash
seq 1000000 |
awk '
BEGIN {
command = "date +%s"
prevTime = 0
}
$1 % 50000 == 0 {
command | getline currentTime
close(command)
print currentTime - prevTime
prevTime = currentTime
}
'
Output:
1335629268
0
0
0
0
0
0
0
0
0
0
0
0
0
0
1
0
0
0
0
Note that the first number is equivalent to date +%s. Just like in your test case, I let the first match be.
Note
*Yes the author is talking about the Korn Shell, not bash as the OP tagged, but bash and ksh are rather similar in a lot of ways. ksh is actually a superset of bash. So I would assume that the read command is not drastically different from one shell to another.

Resources