Labelling of text files in bash loop - bash

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.

Related

Mean of execution time of a program

I have the following bash code (A.cpp, B.cpp and C.txt are filename in the current directory):
#!/bin/bash
g++ A.cpp -o A
g++ B.cpp -o B
Inputfiles=(X Y Z U V)
for j in "${Inputfiles[#]}"
do
echo $j.txt:
i=1
while [ $i -le 5 ]
do
./A $j.txt
./B C.txt
echo ""
i=`expr $i + 1`
done
echo ""
done
rm -f A B
One execution of ./A and ./B is one execution of my program. I run my program 5 times for each input file in the array 'Inputfiles'. I want the average execution time of my program over each input-file. How can I do so?
(Earlier, I tried to add time and clock functions within the A.cpp and B.cpp files, but I am not able to add the execution times of both files to get the execution time of a program.)
If I understand correctly what average you would like to calculate, I think the code below will serve your purpose.
Some explanations on the additions to your script:
Lines 6 - 14 declare a function that expects three arguments and updates the accumulated total time, in seconds
Line 26 initializes variable total_time.
Lines 31, 38, execute programs A and B respectively. Using bash time to collect the execution time. >/dev/null "discards" A's and B's outputs. 2>&1 redirects stderr to stdout so that grep can get time's outputs (a nice explanation can be found here). grep real keeps only the real output from time, you could refer to this post for an explanation of time's output and choose the specific time of your interest. awk {print $2} keeps only the numeric part of grep's output.
Lines 32, 39 store the minutes part to the corresponding variable
Lines 33-34, 40-41 trim the seconds part of real_time variable
Lines 35, 42 accumulate the total time by calling function accumulate_time
Line 46 calculates the average time by dividing with 5
Converted the while loop to a nested for loop and introduced the iterations variable, not necessarily part of the initial question but helps re-usability of the number of iterations
1 #!/bin/bash
2
3 # Function that receives three arguments (total time,
4 # minutes and seconds) and returns the accumulated time in
5 # seconds
6 function accumulate_time() {
7 total_time=$1
8 minutes=$2
9 seconds=$3
10
11 accumulated_time_secs=$(echo "$minutes * 60 + $seconds + $total_time" | bc )
12 echo "$accumulated_time_secs"
13
14 }
15
16 g++ A.cpp -o A
17 g++ B.cpp -o B
18 Inputfiles=(X Y Z U V)
19
20 iterations=5
21
22 for j in "${Inputfiles[#]}"
23 do
24 echo $j.txt:
25 # Initialize total_time
26 total_time=0.0
27
28 for i in $(seq 1 $iterations)
29 do
30 # Execute A and capture its real time
31 real_time=`{ time ./A $j.txt >/dev/null; } 2>&1 | grep real | awk '{print $2}'`
32 minutes=${real_time%m*}
33 seconds=${real_time#*m}
34 seconds=${seconds%s*}
35 total_time=$(accumulate_time "$total_time" "$minutes" "$seconds")
36
37 # Execute B and capture its real time
38 real_time=`{ time ./B C.txt >/dev/null; } 2>&1 | grep real | awk '{print $2}'`
39 minutes=${real_time%m*}
40 seconds=${real_time#*m}
41 seconds=${seconds%s*}
42 total_time=$(accumulate_time "$total_time" "$minutes" "$seconds")
43 echo ""
44 done
45
46 average_time=$(echo "scale=3; $total_time / $iterations" | bc)
47 echo "Average time for input file $j is: $average_time"
48 done
49
50 rm -f A B

Bash - Read lines from file with intervals

I need to read all lines of the file separating at intervals. A function will execute a command with each batch of lines.
Lines range example:
1 - 20
21 - 50
51 - 70
...
I tried with the sed command in a forloop, but the range does not go to the end of the file. For example, a file with 125 lines reads up to 121, missing lines to reach the end.
I commented on the sed line because in this loop the range goes up to 121 and the COUNT is 125.
TEXT=`cat wordlist.txt`
COUNT=$( wc -l <<<$TEXT )
for i in $(seq 1 20 $COUNT);
do
echo "$i"
#sed -n "1","${i}p"<<<$TEXT
done
Output:
1
21
41
61
81
101
121
Thanks!
Quick fix - ensure the last line is processed by throwing $COUNT on the end of of values assigned to i:
for i in $(seq 1 20 $COUNT) $COUNT;
do
echo "$i"
done
1
21
41
61
81
101
121
125
If COUNT happens to be the same as the last value generated by seq then we'll need to add some logic to skip the second time around; for example, if COUNT=121 then we'll want to skip the second time around when i=121, eg:
# assume COUNT=121
lasti=0
for i in $(seq 1 20 $COUNT) $COUNT;
do
[ $lasti = $COUNT ] && break
echo "$i"
lasti=$i
done
1
21
41
61
81
101
121

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.

While loop in bash getting duplicate result

$ cat grades.dat
santosh 65 65 65 65
john 85 92 78 94 88
andrea 89 90 75 90 86
jasper 84 88 80 92 84
santosh 99 99 99 99 99
Scripts:-
#!/usr/bin/bash
filename="$1"
while read line
do
a=`grep -w "santosh" $1 | awk '{print$1}' |wc -l`
echo "total is count of the file is $a";
done <"$filename"
O/p
total is count of the file is 2
total is count of the file is 2
total is count of the file is 2
total is count of the file is 2
total is count of the file is 2
Real O/P should be
total is count of the file is 2 like this right..please let me know,where i am missing in above scripts.
Whilst others have shown you better ways to solve your problem, the answer to your question is in the following line:
a=`grep -w "santosh" $1 | awk '{print$1}' |wc -l`
You are storing names in the variable "line" through the while loop, but it is never used. Instead your loop is always looking for "santosh" which does appear twice and because you run the same query for all 5 lines in the file being searched, you therefore get 5 lines of the exact same output.
You could alter your current script like so:
a=$(grep -w "$line" "$filename" | awk '{print$1}' | wc -l)
The above is not meant to be a solution as others have pointed out, but it does solve your issue.

Nested For loop counter reset

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.

Resources