KSH: Spread commands equally over time - performance

I'm working on a ksh performance script. It takes the following options:
- i (interval time)
- p (messages per interval)
- t (total messages)
So for example: "script -i 1 -p 2 -t 10" means 2 messages per second to a maximum of 10.
This is working, but the script does not yet equally spread the messages over the given interval time. In this example the script should execute the second command after 0.5 seconds, not before.
Is it possible to do this easily?
In short, this is what I have so far:
typeset -i i=0
typeset -i n=0
while [ $i -lt $TOTAAL ]; do
while [ $n -lt $PERINT ]; do
# execute this command
n=$((n+1))
done
i=`expr $i + $PERINT`
# Reset n
n=0
# Wait interval time
sleep $INTERVAL
done
-i: $INTERVAL is an integer (0, 1, 2...) and not calculated in any way. I have done input checks to prevent input the script does not expect.
-t: $TOTAAL
-p: $PERINT

According to serverfault question 340838 Aix 5.3 ships with a separate ksh93 binary in /usr/bin/ksh93.
This will have subsecond sleep and floating point arithmetic. Watch out for fence-post errors, when calculating the interval.

Thanks to Henk Langeveld, I found out ksh93 was pre-installed. This is the new script. It seems to work fine.
typeset -i i=0
typeset -i n=0
while [ $i -lt $TOTAAL ]; do
while [ $n -lt $PERINT ]; do
# execute this command
sleep $VERDELING
n=$((n+1))
done
i=$((i+PERINT))
# Reset n
n=0
done
These are some of the checks I use. I translated the error-messages.
if [[ $PERINT -gt $TOTAAL ]]; then
echo "[[31mFOUT[0m] Messages per interval cannot be greater then total amount"; exit
fi
VERGELIJK=`echo "scale=1; $TOTAAL / $PERINT" | bc`
if [[ $VERGELIJK = *.[1-9]* ]]; then
echo "[[31mFOUT[0m] Messages per interval has to be a multiple of the total amount"; exit
fi
VERDELING=`echo "scale=2; $INTERVAL / $PERINT" | bc`

Related

How can we make a TIMER with (RESET) functionality in bash

I am very new to shell scripting, I have googled many things for this problem. But cannot find a perfect solution to it.
Problem is: It's a log monitoring code with "ERROR", Pattern
How can we make a TIMER in unix scripting. Support I have a timer of 5 minutes, and time keep on decreasing every 1 second. So I want to reset again the timer to 5 minute on certain condition (say $FREQUENCY of ERROR occurrence is greater then say for ex2).
#!/usr/bin/env bash
PATTERN='ERROR'
TIMER=300
FREQUENCY=2
while true;
do grep -i $PATTERN logfile > tmp_log
while [ $TIMER -gt 0 ]
do
sleep 1 &
printf " $TIMER \r" &
TIMER=$[$TIMER-1];
wait
done
if [[ $(wc -l <tmp_log) -ge $FREQUENCY ]]
then
TIMER=300
echo $TIMER
fi
sleep $TIMER
done
#!/bin/bash
PATTERN='ERROR'
TIMER=300
FREQUENCY=2
while true
do
while [ $TIMER -gt 0 ]
do
sleep 1
TIMER=$((TIMER - 1))
grep -i $PATTERN logfile > tmp_log
count=`wc -l tmp_log | awk '{ print $1 }'`
if [ $count -gt $FREQUENCY ]
then
TIMER=300
fi
done
break
done
This is good going for your case.

Check if a process is running with shell script

I'm trying to make a script to see if the process chromium is running.
The script should check every 10 seconds if the process is running and it must end when it finds it 10 times.
Here is my code.
#!/bin/bash
count=0
while true; do
sleep 10s
isthere=`$(top) | grep -w chromium`
if [ $isthere -ne 0 ]; then
count=$((count+1))
fi
if [ $count -eq 10 ]; then
echo "You found the process 10 times"
exit 50
fi
done
I'm having no output whatsoever. I don't know if I'm using the command top properly.
Yep, your usage of top command is incorrect. You try to invoke it from the shell script and it hangs as a result.
You should use top command with some specific options. I suggest you use it with -b option which corresponds to the "batch" mode and -n options which is for the number of iterations top produces its output. For more information check man top.
Also the test for isthere variable should be amended (we check it for non-emptyness).
The resulting script which works is something like this:
#!/bin/bash
count=0
while true; do
sleep 10s
isthere=`top -b -n 1 | grep -w chromium`
if [ -n $isthere ]; then
count=$((count+1))
fi
if [ $count -eq 10 ]; then
echo "You found the process 10 times"
exit 50
fi
done
Please use pgrep.
$ if pgrep ksh >/dev/null; then echo "ksh is running"; fi
ksh is running
In a loop:
i=0
while (( i < 10 )); do
if pgrep ksh >/dev/null; then
(( ++i ))
fi
sleep 10
done
Substitute ksh with the tool of your choice.

Counting down in a loop to zero by the number being given

I am trying to write a while loop to determine the number is being given to count down to 0. Also, if there's no argument given, must display "no parameters given.
Now I have it counting down but the last number is not being 0 and as it is counting down it starts with the number 1. I mush use a while loop.
My NEW SCRIPT.
if [ $# -eq "0" ] ;then
echo "No paramters given"
else
echo $#
fi
COUNT=$1
while [ $COUNT -gt 0 ] ;do
echo $COUNT
let COUNT=COUNT-1
done
echo Finished!
This is what outputs for me.
sh countdown.sh 5
1
5
4
3
2
1
Finished!
I need it to reach to 0
#Slizzered has already spotted your problem in a comment:
You need operator -ge (greater than or equal) rather than -gt (greater than) in order to count down to 0.
As for why 1 is printed first: that's simply due to the echo $# statement before the while loop.
If you're using bash, you could also consider simplifying your code with this idiomatic reformulation:
#!/usr/bin/env bash
# Count is passed as the 1st argument.
# Abort with error message, if not given.
count=${1?No parameters given}
# Count down to 0 using a C-style arithmetic expression inside `((...))`.
# Note: Increment the count first so as to simplify the `while` loop.
(( ++count ))
while (( --count >= 0 )); do
echo $count
done
echo 'Finished!'
${1?No parameters given} is an instance of shell parameter expansion
bash shell arithmetic is documented here.
You should also validate the variable before using it in an arithmetic context. Otherwise, a user can construct an argument that will cause the script to run in an infinite loop or hit the recursion limit and segfault.
Also, don't use uppercase variable names since you risk overriding special shell variables and environment variables. And don't use [ in bash; prefer the superior [[ and (( constructs.
#!/usr/bin/env bash
shopt -s extglob # enables extended globs
if (( $# != 1 )); then
printf >&2 'Missing argument\n'
exit 1
elif [[ $1 != +([0-9]) ]]; then
printf >&2 'Not an acceptable number\n'
exit 2
fi
for (( i = $1; i >= 0; i-- )); do
printf '%d\n' "$i"
done
# or if you insist on using while
#i=$1
#while (( i >= 0 )); do
# printf '%d\n' "$((i--))"
#done
Your code is far from being able to run. So, I don't know where to start to explain. Let's take this small script:
#!/bin/sh
die() {
echo $1 >&2
exit 1;
}
test -z "$1" && die "no parameters given"
for i in $(seq $1 -1 0); do
echo "$i"
done
The main part is the routine seq which does what you need: counting from start value to end value (with increment in between). The start value is $1, the parameter to our script, the increment is -1.
The test line tests whether there is a parameter on the command line - if not, the script ends via the subroutine die.
Hth.
There are a number of ways to do this, but the general approach is to loop from the number given to an ending number decrementing the loop count with each iteration. A C-style for loop works as well as anything. You will adjust the sleep value to get the timing you like. You should also validate the required number and type of input your script takes. One such approach would be:
#!/bin/bash
[ -n "$1" ] || {
printf " error: insufficient input. usage: %s number (for countdown)\n" "${0//*\//}"
exit 1
}
[ "$1" -eq "$1" >/dev/null 2>&1 ] || {
printf " error: invalid input. number '%s' is not an integer\n" "$1"
exit 1
}
declare -i cnt=$(($1))
printf "\nLaunch will occur in:\n\n"
for ((i = cnt; i > 0; i--)); do
printf " %2s\n" "$i"
sleep .5
done
printf "\nFinished -- blastoff!\n\n"
exit 0
Output
$ bash ./scr/tmp/stack/countdown.sh 10
Launch will occur in:
10
9
8
7
6
5
4
3
2
1
Finished -- blastoff!
Your Approach
Your approach is fine, but you need to use the value of COUNT $COUNT in your expression. You also should declare -i COUNT=$1 to tell the shell to treat it as an integer:
#!/bin/bash
if [ $# -eq "0" ] ;then
echo "No paramters given"
else
echo -e "\nNumber of arguments: $#\n\n"
fi
declare -i COUNT=$1
while [ $COUNT -gt 0 ] ;do
echo $COUNT
let COUNT=$COUNT-1
done
echo -e "\nFinished!\n"

Using if and controlling numbers in ksh

I would like to download file with the format cars000.txt, cars003.txt,cars006.txt, till cars105.txt...interval of 3 as you can see
I use the following code in ksh, but after downloading cars012.txt, it fails, it begins to download cars13.txt,...and I don't wish it. What does it fails in the code?
FHR=000
while [ $FHR -le 105 ]
do
file=cars${FHR}.txt
wget http://${dir_target}/${file}
(( FHR = $FHR + 03 ))
echo $FHR
if [[ $FHR -le 10 ]]; then FHR="00"$FHR
else FHR="0"$FHR
fi
done
You should decide: is FHR a string, a decimal or an octal.
You are mixing them currently.
Try the next improvement:
FHR=0
while [ ${FHR} -le 105 ]; do
file=cars${FHR}.txt
(( FHR = FHR + 3 ))
echo Without leading zero: FHR=${FHR}
if [[ $FHR -le 10 ]]; then
echo "FHR=00${FHR}"
else
echo "FHR=0${FHR}"
fi
sleep 1
done
(The next improvement might be using printf or awk and no zero for 102/105)

Shell script read user input

I got stuck on this shell script code where it requires user to enter the group number and find the largest and the average out of that group number.
My code ATM only works with passing the group number as a command-line argument. How to prompt for the group number as user input?
read n
if [ $n -ge 1 ]; then
sum=0
count=$n
max=-1000
if [ $max -lt $1 ]; then
max=$1
fi
while [ $n -ge 1 ]; do
case $1 in
[0-9] | [1-9][0-9])
sum=`expr $sum +$1`;;
-[1-9] | -[1-9][0-9])
sum=`expr $sum + $1`;;
done
if [ $count -gt 0 ]; then
avg=`expr $sum / $count`
echo The largest number is $max
echo The average number is $avg
From your comments it seems you would like to read values from stdin rather than from the command line. To do that in Bash you use the read builtin:
read -ep "Enter group number: " group
printf "Entered %d\n" $group
For interactive prompting it is usually put in the test part of a while loop where you can break if the input is invalid:
shopt -s extglob
while read -ep "Enter group number: " group; do
case $group in
?(-)+([0-9])) # valid input
# compute average here
*) break ;; # not valid input
esac
done
See help read for more information.
You're working way too hard, and your script is way too verbose. Try:
#!/bin/sh
test $# -gt 0 || { echo Please enter at least one argument >&2; exit 1; }
max=0
for x; do
test "$max" -lt $x && max=$x
test $? -gt 1 && exit 1
: $(( count += 1 ))
: $(( sum += x ))
done
echo max = $max
printf "avg = "
expr $sum / $count
Since you used expr in your script, I'm doing the same, but be aware that all of the arithmetic will be done in the integers (so the reported average will be the greatest integer less than the actual average, and non-integer input will be considered an error). This solution relies on test returning a value greater than 1 when it encounters an error (eg, non integer input), which is the behavior specified by the open group.
Also note that this puts the error message on stderr (where error messages belong) and returns a non-zero value to indicate that the script failed.

Resources