Create sequential numbers in loop - bash

I need a bash script which can generate 4500 numbers in sequence and feed it as an input to an executable and repeat the process by creating next 4500 numbers and again feed to the same executable.
The script should exit once more than 90000 numbers are generated.
Right now I am using:
i=1
while [ "$i" -le 90000 ]; do
C:/Python27/Scripts/bu.exe "$i"
i=$(($i+1))
done
which inputs one number at a time and is a time consuming process.
Any help will be gratefully appreciated.
Thanking you and regards.

$ for (( i=1; i<12; i+=3 )); do printf '####\n'; seq "$i" "$(( i+2 ))"; done
####
1
2
3
####
4
5
6
####
7
8
9
####
10
11
12
Replace the numbers above with your real values and depending on what it is you're really trying to do either pipe the seq output to your command:
$ for (( i=1; i<12; i+=3 )); do seq "$i" "$(( i+2 ))" | my_command; done
or call your command with the seq output as it's arguments:
$ for (( i=1; i<12; i+=3 )); do my_command $(seq "$i" "$(( i+2 ))"); done

I would rather advise you to execute it parallel like below. My below modification will execute 10 processes in parallel and will reduce your execution time. Please keep in mind parallel execution also depends on number of processors and memory in your system.
i=1
j=0
while [ "$i" -le 90000 ]; do
C:/Python27/Scripts/bu.exe "$i" &
# Now you are executing parallel
j=$(($j+1))
#if 10 parallel process has been created just wait to complete them
# and re-start the parallel process again
if [ $j -ge 10 ]; then
wait
j=0
fi
i=$(($i+1))
done

I read somewhere that running $(seq) is expensive (in the context of that discussion, not related to this question) so I decided to test running the seq while the bu.exe is running in the background:
for (( i=1 ; $((j=i+4499))<=90000 ; i+=4500)) # 1...4500, 4501...9000 etc.
do
parms=$(seq -s " " $i $j) # set the parameters to var
wait $pid # wait for the previous bu.exe
bu.exe "$parms" & # execute bu.exe in the bg
pid=$! # store pid
done

Related

echo help for bash script to count seconds (and overwrite)

I'd like to have a counter on my terminal prompt. I pass it a value from the command line in minutes. It counts the seconds on the screen, without a newline, in other words, it shows 0, 1, 2, 3 etc...but overwriting the previous number. How can I do that? The script below does a newline for each second.
#!/bin/bash
i=0
seconds=$1*60
while (( $i < $seconds ))
do
echo $i
sleep 1
((i++))
done
You can adjust the width with a * operator:
while (( i < seconds )); do
printf "\r%*d" ${#seconds} $i
sleep 1
((i++))
done

Recursive calls in bash (catalan-numbers)

Im trying to create a program that lists all catalan-numbers below or equal to an argument in a bash-script. This is what I currently have but its giving me a stackoverflow error (I believe the error must be in the for-loop, but I can't figure out why). I have made this program in java and it works so I think it must be some syntax error?
#!/usr/bin/env bash
pcat=0
Cat() {
res=0
if [ $1 -le 1 ]
then
echo 1
return 1
fi
for ((i=0; i<$1; i++))
do
var1=$(($1-($i+1)))
call1=$(Cat $i)
call2=$(Cat $var1)
res=$(( res+call1+call2 ))
done
echo ${res}
return res
}
while [ $pcat -lt $1 ]
do
Cat $pcat
pcat=$((pcat+1))
done
The line where you do return res is incorrect, return could deal only with numbers and number less than 128 in general.
Assuming what you meant was return $res, the script will run.
I managed to get the program working with a similar code to yours:
#!/bin/bash
catalan() {
local n=$1
#echo "called with $n" >&2
if (( n <= 1 )); then
res=1
else
res=0
for ((i=0; i<n; i++))
do
var1=$(( n-i-1 ))
call1=$(catalan $i)
call2=$(catalan $var1)
res=$(( res+call1*call2 ));
#echo ":$i:$var1: result Call1:$call1: and Call2:$call2: $res" >&2
done
fi
#echo "result is ${res}" >&2
echo "$res"
}
n=$1
until (( pcat > n ))
do catalan "$((pcat++))"
done
echo "all was done"
There was a second problem in that the values of Call1 and Call2 need to be multiplied, not added. Changed res+call1+call2 to:
res=$(( res+call1*call2 ))
But the resultant code was very slow. Just to calculate the tenth (10) catalan number the code took 16 seconds.
An entirely new program that keeps the values inside a single array: catarray.
As this:
#!/bin/bash
# some initial values to jump start the code:
catarray=( 1 1 2 5 )
#############################################################################
catalan(){
#echo "making call for $1" >&2
local n=$1
# ${#catarray[#]} is the count of values in catarray (last index + 1).
# if the number n to be found is not yet in the array of values of
# catarray then we need to calculate it. Else, we just print the value.
if (( n >= ${#catarray[#]} )); then
#echo "$n is bigger than ${#catarray[#]}" >&2
# this is a new number, lets loop up till we
# fill the array just up to this value
for (( i=${#catarray[#]};i<=n;i++)); do
#echo "fill index $i in array" >&2
# calculate the sum of all terms for catalan of $n.
for(( j=0;j<i;j++ )); do
(( catarray[i] += catarray[j] * catarray[i-j-1] ))
#echo "done math in $i for $j with ${catarray[j]} *
#echo "* ${catarray[i-j-1]} = ${catarray[i]}"
done
done
fi
# After making the math or else we just print the known value.
#printf 'result of catalan number is %s\n' "${catarray[n]}"
}
#############################################################################
catalan "$1"
printf '%s, ' "${catarray[#]}"; echo
Wich will execute the tenth (10) catalan number in just 4 milliseconds.
A lot of echos were included to "see" how the program works. You may unquote them.
There is a limit though, numbers in bash should fit in 64 bits (for 64 bit computers) or be less than (2^63-1). That makes the biggest catalan number possible the 35th.
$ catalan 35
1, 1, 2, 5, 14, 42, 132, 429, 1430, 4862, 16796, 58786, 208012, 742900,
2674440, 9694845, 35357670, 129644790, 477638700, 1767263190,
6564120420, 24466267020, 91482563640, 343059613650, 1289904147324,
4861946401452, 18367353072152, 69533550916004, 263747951750360,
1002242216651368, 3814986502092304, 14544636039226909,
55534064877048198, 212336130412243110, 812944042149730764,
3116285494907301262
But will only take ~20 miliseconds to do that.

Bash Script accepting a number, then printing a set of int from 0 to the number entered

I am trying to write a bash script that accepts a number from the keyboard, and then prints a set of integers from 0 to the number entered. I can't figure out how to do it at all.
This is my code:
while [ 1 ]
do
echo -n "Enter a color: "
read user_answer
for (( $user_answer = $user_answer; $user_answer>0; $user_answer--))
echo $user_answer
fi
done
exit
The error I'm recieving is:
number_loop: line 10: syntax error near unexpected token echo'
number_loop: line 10: echo $user_answer'
Assign a separate variable in order to use increment/decrement operators. $user_answer=$user_answer will always be true and it will throw an error when trying to use decrement. Try the following :
#!/bin/bash
while [ 1 ]
do
echo -n "Enter a color: "
read user_answer
for (( i=$user_answer; i>0; i-- ))
do
echo $i
done
done
exit
You missed the do statement between your for and the echo.
bash has many options to write numbers. What you seem to be trying to do is easiest done with seq:
seq $user_answer -1 0
If you want to use your loop, you have to insert a ; do and replace the fi with done, and replace several $user_answer:
for (( i = $user_answer; i>0; i--)); do
echo $i
done
(btw: I assumed that you wanted to write the numbers in reverse order, as you are going backwards in your loop. Forwards is even easier with seq:
seq 0 $user_input
)
This is where a c-style loop works particularly well:
#!/bin/bash
for ((i = 1; i <= $1; i++)); do
printf "%s\n" "$i"
done
exit 0
Example
$ bash simplefor.sh 10
1
2
3
4
5
6
7
8
9
10
Note: <= is used as the for loop test so it 10 it iterates 1-10 instead of 0-9.
In your particular case, iterating from $user_answer you would want:
for (( i = $user_answer; i > 0; i--)); do
echo $i
done
The for loop is a bash internal command, so it doesn't fork a new process.
The seq command has a nice, one-line syntax.
To get the best of the twos, you can use the { .. } syntax:
eval echo {1..$answer}

Comparison between array items

I've written a script to calculate the bandwidth usage of an OpenVZ container over time and suspend it if it uses too much too quickly. Here is the script so far:
#!/bin/bash
# Thresholds are in bytes per second
LOGDIR="/var/log/outbound_ddos"
THRESHOLD1=65536
THRESHOLD2=117964
while [ 1 ]
do
for veid in $(/usr/sbin/vzlist -o veid -H)
do
# Create the log file if it doesn't already exist
if ! test -e $LOGDIR/$veid.log; then
touch $LOGDIR/$veid.log
fi
# Parse out the inbound/outbound traffic and assign them to the corresponding variables
eval $(/usr/sbin/vzctl exec $veid "grep venet0 /proc/net/dev" | \
awk -F: '{print $2}' | awk '{printf"CTOUT=%s\n", $9}')
# Print the output and a timestamp to a log file
echo $(date +%s) $CTOUT >> $LOGDIR/$veid.log
# Read last 10 entries into arrays
i=0
tail $LOGDIR/$veid.log | while read time byte
do
times[i]=$time
bytes[i]=$byte
let ++i
done
# Time checks & calculations for higher threshold
counter=0
for (( i=0; i<9; i++ ))
do
# If we have roughly the right timestamp
if (( times[9-i] < times[8-i] + 20 ))
then
# If the user has gone over the threshold
if (( bytes[9-i] > bytes[8-i] + THRESHOLD2 * 10 ))
then let ++counter
fi
fi
done
# Now check counter
if (( counter == 9 ))
then vzctl stop $veid
fi
# Same for lower threshold
counter=0
for (( i=0; i<3; i++ ))
do
# If we have roughly the right timestamp
if (( times[3-i] < times[2-i] + 20 ))
then
# If the user has gone over the threshold
if (( bytes[3-i] > bytes[2-i] + THRESHOLD1 * 10 ))
then let ++counter
fi
fi
done
# Now check counter
if (( counter == 2 ))
then vzctl stop $veid
fi
done
sleep 10
done
I've checked the numbers in /var/log/outbound_ddos/vm101.log and they're increasing by more than the threshold, but nothing is happening.
I added some echo statements to try and figure out where the problem is and it seems to be this comparison that's returning false:
if (( bytes[9-i] > bytes[8-i] + THRESHOLD2 * 10 ))
So then I tried the following, which printed out nothing:
echo ${bytes[9-i]}
Could anyone point me in the right direction? I think the script is nearly done, probably something very simple.
Your shell runs the while read loop in a subshell (see here for why it does not work as expected), so your array magic does not propagate outside the tail | while construct.
Read this and fix accordingly :-)

generate a random file using shell script

How can i generate a random file filled with random number or character in shell script? I also want to specify size of the file.
Use dd command to read data from /dev/random.
dd if=/dev/random of=random.dat bs=1000000 count=5000
That would read 5000 1MB blocks of random data, that is a whole 5 gigabytes of random data!
Experiment with blocksize argument to get the optimal performance.
head -c 10 /dev/random > rand.txt
change 10 to whatever. Read "man random" for differences between /dev/random and /dev/urandom.
Or, for only base64 characters
head -c 10 /dev/random | base64 | head -c 10 > rand.txt
The base64 might include some characters you're not interested in, but didn't have time to come up with a better single-liner character converter...
(also we're taking too many bytes from /dev/random. sorry, entropy pool!)
A good start would be:
http://linuxgazette.net/153/pfeiffer.html
#!/bin/bash
# Created by Ben Okopnik on Wed Jul 16 18:04:33 EDT 2008
######## User settings ############
MAXDIRS=5
MAXDEPTH=2
MAXFILES=10
MAXSIZE=1000
######## End of user settings ############
# How deep in the file system are we now?
TOP=`pwd|tr -cd '/'|wc -c`
populate() {
cd $1
curdir=$PWD
files=$(($RANDOM*$MAXFILES/32767))
for n in `seq $files`
do
f=`mktemp XXXXXX`
size=$(($RANDOM*$MAXSIZE/32767))
head -c $size /dev/urandom > $f
done
depth=`pwd|tr -cd '/'|wc -c`
if [ $(($depth-$TOP)) -ge $MAXDEPTH ]
then
return
fi
unset dirlist
dirs=$(($RANDOM*$MAXDIRS/32767))
for n in `seq $dirs`
do
d=`mktemp -d XXXXXX`
dirlist="$dirlist${dirlist:+ }$PWD/$d"
done
for dir in $dirlist
do
populate "$dir"
done
}
populate $PWD
Create 100 randomly named files of 50MB in size each:
for i in `seq 1 100`; do echo $i; dd if=/dev/urandom bs=1024 count=50000 > `echo $RANDOM`; done
The RANDOM variable will give you a different number each time:
echo $RANDOM
Save as "script.sh", run as ./script.sh SIZE. The printf code was lifted from http://mywiki.wooledge.org/BashFAQ/071. Of course, you could initialize the mychars array with brute force, mychars=("0" "1" ... "A" ... "Z" "a" ... "z"), but that wouldn't be any fun, would it?
#!/bin/bash
declare -a mychars
for (( I=0; I<62; I++ )); do
if [ $I -lt 10 ]; then
mychars[I]=$I
elif [ $I -lt 36 ]; then
D=$((I+55))
mychars[I]=$(printf \\$(($D/64*100+$D%64/8*10+$D%8)))
else
D=$((I+61))
mychars[I]=$(printf \\$(($D/64*100+$D%64/8*10+$D%8)))
fi
done
for (( I=$1; I>0; I-- )); do
echo -n ${mychars[$((RANDOM%62))]}
done
echo

Resources