In general, we see programs running and showing from how long it running in realtime
example
[#] Scanning is running from 1min 30sec
The value will keep change dynamically untill the script complete.
How can we do it in bash !
By using printf with a carriage return but no newline
count=0
SECONDS=0
while ((count < 10)); do
# computation code goes here
# just use a sleep for demonstration
sleep 1
printf "\rRunning for %d seconds" $SECONDS
((count++))
done
echo # just to add a newline in the output
SECONDS is a special variable in bash
Related
I have a bash script and at certain points I am using echo to put some messages in a log file. The problem that I have is related to the DATE variable which will be static throughout the entire execution of the script.
I have this basic script below to illustrate the problem:
#!/bin/bash
DATE=`date +"%Y-%m-%dT%H:%M:%S%:z"`
echo "script started at $DATE"
echo "doing something"
sleep 2
echo "script finished at $DATE"
If I execute this script, the output of the $DATE variable is the same in both lines. Is there some bash magic that could nicely resolve this without having to replace $DATE with the command itself on each line?
Thanks in advance
Newer versions of the bash/printf builtin have support for generating datetime stamps without the need to spawn a subprocess to call date:
$ builtin printf --help
...snip...
Options:
-v var assign the output to shell variable VAR rather than
display it on the standard output
...snip...
In addition to the standard format specifications described in printf(1),
printf interprets:
%b expand backslash escape sequences in the corresponding argument
%q quote the argument in a way that can be reused as shell input
%(fmt)T output the date-time string resulting from using FMT as a format
string for strftime(3)
... snip ...
Instead of spawning a subprocess to call date, eg:
logdt=`date +"%Y-%m-%dT%H:%M:%S:%z"`
The same can be accomplished via printf -v by wrapping the desired format in %(...)T, eg:
printf -v logdt '%(%Y-%m-%dT%H:%M:%S:%z)T'
NOTE: assuming %:z should be :%z
Assuming you'll be tagging a lot of lines with datetime stamps then the savings from eliminating the subproces date calls could be huge.
Running a test of 1000 datetime stamp generations:
$ time for ((i=1;i<=1000;i++)); do { printf -v logdt '%(...)T' | logdate=$(date ...) }; done
Timings for printf -v logdt '%(...)T':
real 0m0.182s # ~130 times faster than $(date ...)
user 0m0.171s
sys 0m0.000s
Timings for logdt=$(date ...):
real 0m24.443s # ~130 times slower than printf -v
user 0m5.533s
sys 0m16.724s
With bash version 4.3+ , you can use the builtin printf to format datetimes. -1 below is a magic value that means "now".
#!/bin/bash
datefmt='%Y-%m-%dT%H:%M:%S%z'
printf "script started at %($datefmt)T\n" -1
echo "doing something"
sleep 2
printf "script finished at %($datefmt)T\n" -1
bash didn't recognize %:z for me.
This can help you:
#!/bin/bash
echo "script started at $(date +'%Y-%m-%dT%H:%M:%S%:z')"
echo "doing something"
sleep 2
echo "script finished at $(date +'%Y-%m-%dT%H:%M:%S%:z')"
You might want to create an alias if calling the full command looks clumsy to you.
I am implementing monitor_log function which will tail the most recent line from running log and check required string with while loop, the timeout logic should be when the tail log running over 300 seconds, it must close the tail and while loop pipeline.
The big issue i found is for some server the running log NOT keep generating, which means tail -n 1 -f "running.log" will also NOT generate output for while loop to consume, hence the timeout checking logic if [[ $(($SECONDS - start_timer)) -gt 300 ]] will not hit properly.
e.g I set 300 seconds to timeout, but if running.log stopped generate new line before 300 seconds and no more new line in 30 minutes, tail will not generate new output in 30 minutes, hence timeout checking logic in while loop not hit in 30 minutes, so even after 300 seconds it keep tailing and not break out, and if no new line coming from running.log forever, the timeout checking logic will not hit forever.
function monitor_log() {
if [[ -f "running.log" ]]; then
# Timer start
start_timer=$SECONDS
# Tail the running log last line and keep check required string
tail -n 1 -f "running.log" | while read tail_line
do
if [[ $(($SECONDS - start_timer)) -gt 300 ]]; then
break;
fi
if [[ "$tail_line" == "required string" ]]; then
capture_flag=1
fi
if [[ $capture_flag -eq 1 ]]; then
break;
fi
done
fi
}
Could you help to figure out the proper way to timeout the tail and while loop when 300 seconds ? Thank you.
Two options worth considering for inactivity timeout. Usually, option #1 works better.
Option 1: Use timeout (read -t timeout).
It will cap the the 'read' time. See information from bash man. The timeout will cause the read to fail, breaking the whlie loop.
In the code above, replace
tail -n 1 -f "running.log" | while read tail_line
with
tail -n 1 -f "running.log" | while read -t 300 tail_line
Option 2: TMOUT envvar
It's possible to get same effect by setting TMOUT env var.
From bash man - 'read' command:
-t timeout
Cause read to time out and return failure if a complete line of input (or a specified number of characters) is not
read within timeout seconds. timeout may be a decimal number with a
fractional
portion following the decimal point. This option is only effective if read is reading input from a terminal,
pipe, or other special file; it has no effect when reading from
regular files. If
read times out, read saves any partial input read into the specified variable name. If timeout is 0, read returns
immediately, without trying to read any data. The exit status is 0 if
input is
available on the specified file descriptor, non-zero otherwise. The exit status is greater than 128 if the
timeout is exceeded.
Based on dash-o's answer I did test for option 1, the -t for read command works fine only when while read loop on main shell and tail in sub shell, in my question, the tail in main shell, and while read loop consume its output in subshell, in this condition, even setup -t for read command, script not stop when time used up. Refer to
Monitoring a file until a string is found, Bash tail -f with while-read and pipe hangs and How to [constantly] read the last line of a file?
The working code based on dash-o's solution below:
function monitor_log() {
if [[ -f "running.log" ]]; then
# Tail the running log last line and keep check required string
while read -t 300 tail_line
do
if [[ "$tail_line" == "required string" ]]; then
capture_flag=1
fi
if [[ $capture_flag -eq 1 ]]; then
break;
fi
done < <(tail -n 1 -f "running.log")
# Silently kill the remained tail process
tail_pid=$(ps -ef | grep 'tail' | cut -d' ' -f5)
kill -13 $tail_pid
fi
}
But as test, this function after timeout auto terminate will left tail process alive, we can observe PID by check ps -ef on console, need to kill tail_PID separately.
Also test another solution: not change tail and while read loop position, so tail still on main shell and while read loop keep in sub shell after | pipeline, the only change is adding GNU's timeout command before tail command, it works perfect and no tail process left after timeout auto terminate:
function monitor_log() {
if [[ -f "running.log" ]]; then
# Tail the running log last line and keep check required string
timeout 300 tail -n 1 -f "running.log" | while read tail_line
do
if [[ "$tail_line" == "required string" ]]; then
capture_flag=1
fi
if [[ $capture_flag -eq 1 ]]; then
break;
fi
done
fi
}
I want to repeatedly run a program for a maximum of 5 seconds.
I know that timeout executes a command for the amount of time specified, e.g.:
timeout 5 ./a.out
But I want to keep executing the program until 5 seconds have passed so I can tell how
many times it was executed.
I figured that I need something like this:
timeout 5 `while true; do ./a.out; done`
but this is not working. I've already tried to create a shell script that calculates
the elapsed time of every loop iteration and subtracts it from the start time,
but that is inefficient.
Any help would be appreciated.
If you want to use timeout:
timeout 5s ./a.out
You can write a short script and easily set an end time with date -d "date string" +%s to get a future time in seconds. Then just compare current time to end time and break on true. This allows you to capture additional data during your execution time period. For example, the following code sets the end time 5 seconds in the future and then loops until current time equals end.
#!/bin/bash
end=$(date -d "+ 5 seconds" +%s) # set end time with "+ 5 seconds"
declare -i count=0
while [ $(date +%s) -lt $end ]; do # compare current time to end until true
((count++))
printf "working... %s\n" "$count" # do stuff
sleep .5
done
output:
$ bash timeexec.sh
working... 1
working... 2
working... 3
working... 4
working... 5
working... 6
working... 7
working... 8
working... 9
In your case you would do something like
./a.out & # start your application in background
apid=$(pidof a.out) # save PID of a.out
while [ $(date +%s) -lt $end ]; do
# do stuff, count, etc.
sleep .5 # something to prevent continual looping
done
kill $apid # kill process after time test true
I'm working on a linux server which is sometimes very slow. So when i add some jobs to run for me i have to wait for a few hours just to run a simple calculation.
I was wondering if i am able to start the next analysis but let it wait until the output of the previous analysis is there. (the second analysis needs the first analysis output)
I tried to make except and other options working but still no success (found except and other options in previous question on stackoverflow):
expect {
'output/analysis_file1.txt'
}
Any ideas/hints are appreciated and will help me allot.
The only thing i want is to let the second scrip wait till the text file of the first script is given.
The 4 scripts:1.
#!/bin/bash
#$ -cwd
./script1.sh
. ./script2.sh $repla
. ./script3.sh $replac
2:
repla=''
for i in 'abcdefghijklmnopqrst'
do
repla=`echo $i | sed 's/'abc'/'xyz'/g'`
#echo $repla
done
3:
replac=''
for j in $1
do
replac=`echo $j | sed 's/'xyz'/'san'/g'`
#echo $replac
done
4:
replace=''
for h in $1
do
replace=`echo $h | sed 's/'san'/'sander'/g'`
#echo $replace
done
you can use below core with some modifications
#!/bin/bash
while [ ! -f FILE_NAME ]
do
sleep SOME_SECONDS
done
echo "file found"
You can use wait if you know the pid of the process running in background. Wait will also return the same exit code of the process it is waiting to stop.
firstProcess & # Running in background
firstPid=$!
otherProcess # Concurrent with firstProcess
wait $firstPid # Wait firstProcess finish
anotherProcess
Instead of executing multiple scripts independently you should create a master script runner like this:
#!/bin/bash
# sanity checks & parse arguments
./script1
ret=$?
# check for return value of script1 using $ret variable
./script2
ret=$?
# check for return value of script2 using $ret variable
./script3
ret=$?
# check for return value of script3 using $ret variable
...
# do cleanup and reporting
Did some search online, found simple 'tutorials' to use named pipes. However when I do anything with background jobs I seem to lose a lot of data.
[[Edit: found a much simpler solution, see reply to post. So the question I put forward is now academic - in case one might want a job server]]
Using Ubuntu 10.04 with Linux 2.6.32-25-generic #45-Ubuntu SMP Sat Oct 16 19:52:42 UTC 2010 x86_64 GNU/Linux
GNU bash, version 4.1.5(1)-release (x86_64-pc-linux-gnu).
My bash function is:
function jqs
{
pipe=/tmp/__job_control_manager__
trap "rm -f $pipe; exit" EXIT SIGKILL
if [[ ! -p "$pipe" ]]; then
mkfifo "$pipe"
fi
while true
do
if read txt <"$pipe"
then
echo "$(date +'%Y'): new text is [[$txt]]"
if [[ "$txt" == 'quit' ]]
then
break
fi
fi
done
}
I run this in the background:
> jqs&
[1] 5336
And now I feed it:
for i in 1 2 3 4 5 6 7 8
do
(echo aaa$i > /tmp/__job_control_manager__ && echo success$i &)
done
The output is inconsistent.
I frequently don't get all success echoes.
I get at most as many new text echos as success echoes, sometimes less.
If I remove the '&' from the 'feed', it seems to work, but I am blocked until the output is read. Hence me wanting to let sub-processes get blocked, but not the main process.
The aim being to write a simple job control script so I can run say 10 jobs in parallel at most and queue the rest for later processing, but reliably know that they do run.
Full job manager below:
function jq_manage
{
export __gn__="$1"
pipe=/tmp/__job_control_manager_"$__gn__"__
trap "rm -f $pipe" EXIT
trap "break" SIGKILL
if [[ ! -p "$pipe" ]]; then
mkfifo "$pipe"
fi
while true
do
date
jobs
if (($(jobs | egrep "Running.*echo '%#_Group_#%_$__gn__'" | wc -l) < $__jN__))
then
echo "Waiting for new job"
if read new_job <"$pipe"
then
echo "new job is [[$new_job]]"
if [[ "$new_job" == 'quit' ]]
then
break
fi
echo "In group $__gn__, starting job $new_job"
eval "(echo '%#_Group_#%_$__gn__' > /dev/null; $new_job) &"
fi
else
sleep 3
fi
done
}
function jq
{
# __gn__ = first parameter to this function, the job group name (the pool within which to allocate __jN__ jobs)
# __jN__ = second parameter to this function, the maximum of job numbers to run concurrently
export __gn__="$1"
shift
export __jN__="$1"
shift
export __jq__=$(jobs | egrep "Running.*echo '%#_GroupQueue_#%_$__gn__'" | wc -l)
if (($__jq__ '<' 1))
then
eval "(echo '%#_GroupQueue_#%_$__gn__' > /dev/null; jq_manage $__gn__) &"
fi
pipe=/tmp/__job_control_manager_"$__gn__"__
echo $# >$pipe
}
Calling
jq <name> <max processes> <command>
jq abc 2 sleep 20
will start one process.
That part works fine. Start a second one, fine.
One by one by hand seem to work fine.
But starting 10 in a loop seems to lose the system, as in the simpler example above.
Any hints as to what I can do to solve this apparent loss of IPC data would be greatly appreciated.
Regards,
Alain.
Your problem is if statement below:
while true
do
if read txt <"$pipe"
....
done
What is happening is that your job queue server is opening and closing the pipe each time around the loop. This means that some of the clients are getting a "broken pipe" error when they try to write to the pipe - that is, the reader of the pipe goes away after the writer opens it.
To fix this, change your loop in the server open the pipe once for the entire loop:
while true
do
if read txt
....
done < "$pipe"
Done this way, the pipe is opened once and kept open.
You will need to be careful of what you run inside the loop, as all processing inside the loop will have stdin attached to the named pipe. You will want to make sure you redirect stdin of all your processes inside the loop from somewhere else, otherwise they may consume the data from the pipe.
Edit: With the problem now being that you are getting EOF on your reads when the last client closes the pipe, you can use jilles method of duping the file descriptors, or you can just make sure you are a client too and keep the write side of the pipe open:
while true
do
if read txt
....
done < "$pipe" 3> "$pipe"
This will hold the write side of the pipe open on fd 3. The same caveat applies with this file descriptor as with stdin. You will need to close it so any child processes dont inherit it. It probably matters less than with stdin, but it would be cleaner.
As said in other answers you need to keep the fifo open at all times to avoid losing data.
However, once all writers have left after the fifo has been open (so there was a writer), reads return immediately (and poll() returns POLLHUP). The only way to clear this state is to reopen the fifo.
POSIX does not provide a solution to this but at least Linux and FreeBSD do: if reads start failing, open the fifo again while keeping the original descriptor open. This works because in Linux and FreeBSD the "hangup" state is local to a particular open file description, while in POSIX it is global to the fifo.
This can be done in a shell script like this:
while :; do
exec 3<tmp/testfifo
exec 4<&-
while read x; do
echo "input: $x"
done <&3
exec 4<&3
exec 3<&-
done
Just for those that might be interested, [[re-edited]] following comments by camh and jilles, here are two new versions of the test server script.
Both versions now works exactly as hoped.
camh's version for pipe management:
function jqs # Job queue manager
{
pipe=/tmp/__job_control_manager__
trap "rm -f $pipe; exit" EXIT TERM
if [[ ! -p "$pipe" ]]; then
mkfifo "$pipe"
fi
while true
do
if read -u 3 txt
then
echo "$(date +'%Y'): new text is [[$txt]]"
if [[ "$txt" == 'quit' ]]
then
break
else
sleep 1
# process $txt - remember that if this is to be a spawned job, we should close fd 3 and 4 beforehand
fi
fi
done 3< "$pipe" 4> "$pipe" # 4 is just to keep the pipe opened so any real client does not end up causing read to return EOF
}
jille's version for pipe management:
function jqs # Job queue manager
{
pipe=/tmp/__job_control_manager__
trap "rm -f $pipe; exit" EXIT TERM
if [[ ! -p "$pipe" ]]; then
mkfifo "$pipe"
fi
exec 3< "$pipe"
exec 4<&-
while true
do
if read -u 3 txt
then
echo "$(date +'%Y'): new text is [[$txt]]"
if [[ "$txt" == 'quit' ]]
then
break
else
sleep 1
# process $txt - remember that if this is to be a spawned job, we should close fd 3 and 4 beforehand
fi
else
# Close the pipe and reconnect it so that the next read does not end up returning EOF
exec 4<&3
exec 3<&-
exec 3< "$pipe"
exec 4<&-
fi
done
}
Thanks to all for your help.
Like camh & Dennis Williamson say don't break the pipe.
Now I have smaller examples, direct on the command line:
Server:
(
for i in {0,1,2,3,4}{0,1,2,3,4,5,6,7,8,9};
do
if read s;
then echo ">>$i--$s//";
else
echo "<<$i";
fi;
done < tst-fifo
)&
Client:
(
for i in {%a,#b}{1,2}{0,1};
do
echo "Test-$i" > tst-fifo;
done
)&
Can replace the key line with:
(echo "Test-$i" > tst-fifo&);
All client data sent to the pipe gets read, though with option two of the client one may need to start the server a couple of times before all data is read.
But although the read waits for data in the pipe to start with, once data has been pushed, it reads the empty string forever.
Any way to stop this?
Thanks for any insights again.
On the one hand the problem is worse than I thought:
Now there seems to be a case in my more complex example (jq_manage) where the same data is being read over and over again from the pipe (even though no new data is being written to it).
On the other hand, I found a simple solution (edited following Dennis' comment):
function jqn # compute the number of jobs running in that group
{
__jqty__=$(jobs | egrep "Running.*echo '%#_Group_#%_$__groupn__'" | wc -l)
}
function jq
{
__groupn__="$1"; shift # job group name (the pool within which to allocate $__jmax__ jobs)
__jmax__="$1"; shift # maximum of job numbers to run concurrently
jqn
while (($__jqty__ '>=' $__jmax__))
do
sleep 1
jqn
done
eval "(echo '%#_Group_#%_$__groupn__' > /dev/null; $#) &"
}
Works like a charm.
No socket or pipe involved.
Simple.
run say 10 jobs in parallel at most and queue the rest for later processing, but reliably know that they do run
You can do this with GNU Parallel. You will not need a this scripting.
http://www.gnu.org/software/parallel/man.html#options
You can set max-procs "Number of jobslots. Run up to N jobs in parallel." There is an option to set the number of CPU cores you want to use. You can save the list of executed jobs to a log file, but that is a beta feature.