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
Related
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
For example,
I have output every second like
user#pc:
A
B
C
D
E
I want the output refresh from A to E in the same line(no new lines being created)
Thanks!
You can use the carriage return character "\r". Check the difference between:
for x in $(seq 10); do printf "$x"; sleep 1; done
and
for x in $(seq 10); do printf "$x\r"; sleep 1; done
Depending upon where your input is coming from.
I would employ the below techniques.
For files:
#!/bin/bash
while read line
do
echo -e "\e[1A" # moving the cursor back to the previously printed line
sleep 1s;
echo -ne "$line\e[K" # \e[K cleans the residues of the previous output.
done <file_name
echo
For arrays:
#!/bin/bash
arr=(12 11 10 9 8 7 6 5 4 3 2 1 0)
for i in ${arr[#]}
do
echo -e "\e[1A"
sleep 1s;
echo -ne "Waiting time : "$i" Seconds\e[K"
done
echo
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}
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 :-)
I tried to create a shell script, which sum the given numbers. If there is no given parameter, then it tries to read the pipe output, but I get an error.
#!/bin/sh
sum=0
if [ $# -eq 0 ]
then
while read data
do
sum=`expr $sum + $data`
done
else
for (( i = 1 ; i <= $#; i++ ))
do
sum=`expr $sum + ${!i}`
done
fi
echo $sum
This works: sum 10 12 13
But this one doesn't: echo 10 12 13| sum
Thanks in advance,
Here you go (assuming bash, not sh):
#!/bin/bash
sum=0
if (( $# == 0 )); then
# Read line by line
# But each line might consist of separate numbers to be added
# So read each line as an array!
while read -a data; do
# Now data is an array... but if empty, continue
(( ${#data[#]} )) || continue
# Convert this array into a string s, with elements separated by a +
printf -v s "%s+" ${data[#]}
# Append 0 to s (observe that s ended with a +)
s="${s}0"
# Add these numbers to sum
(( sum += s ))
done
else
# If elements come from argument line, do the same!
printf -v s "%s+" $#
# Append 0 to s (observe that s ended with a +)
s="${s}0"
# Add these numbers to obtain sum
(( sum = s ))
fi
echo $sum
You can invoke it thus:
$ echo 10 12 13 | ./sum
35
$ ./sum 10 12 13
35
$ # With several lines and possibly empty lines:
$ { echo 10 12 13; echo; echo 42 22; } | ./sum
99
Hope this helps!
Edit. You might also be interested in learning cool stuff about IFS. I've noticed that people tend to confuse # and * in bash. If you don't know what I'm talking about, then you should use # instead of *, also for array subscripts! In the bash manual, you'll find that when double quoted, $* (or ${array[*]}) expands to all the elements of the array separated by the value of the IFS. This can be useful in our case:
#!/bin/bash
sum=0
if (( $# == 0 )); then
# Read line by line
# But each line might consist of separate numbers to be added
# So read each line as an array!
while read -a data; do
# Now data is an array... but if empty, continue
(( ${#data[#]} )) || continue
# Setting IFS=+ (just for the sum) will yield exactly what I want!
IFS=+ sum=$(( sum + ${data[*]} ))
done
else
# If elements come from argument line, do the same!
# Setting IFS=+ (just for the sum) will yield exactly what I want!
IFS=+ sum=$(( $* ))
fi
echo $sum
Gniourf now exits from teacher mode. :-)