I've got some long-running commands that can last 15 minutes or more.
Is there a way to output a dynamic "time spent" section in the output?
E.g.
$ `do something long`
10 mins 15 secs
Refreshing state...
then:
$ `do something long`
14 mins 15 secs
Refreshing state...
Output results
$ ... and we're done
zmodload -F zsh/system p:sysparams # Load the sysparams table.
zmodload -F zsh/terminfo b:echoti # Load the echoti builtin.
timed() {
local +h -i SECONDS=0
# Open an async process for the timer.
local -i timer_fd
exec {timer_fd}< <(
# Print the process id, so we can kill it later.
print $sysparams[pid]
echoti civis # Make the cursor invisible.
while true; do
print "$(( SECONDS / 60 )) mins, $(( SECONDS % 60 )) secs"
echoti cuu1 # Move the cursor up 1 line.
done
)
# Read the timer process id.
local -i timer_pid
read timer_pid <&$timer_fd
# Start another async process for the actual task.
local -i task_fd
exec {task_fd}< <(
eval "$#" # Run the task.
# Close the timer fd and kill the timer process.
exec {timer_fd}<&-
kill -KILL $timer_pid
echoti cnorm # Make the cursor normal again.
)
# Redirect the output of the async processes to the terminal. This is
# a blocking operation. Thus, we won't get past these two lines until
# both async processes end.
<&$timer_fd
<&$task_fd
exec {task_fd}<&- # We're done. Close the task fd.
}
Example usage:
% timed 'sleep 855; print Output results'
References
Process substitution (<( command ))
Opening ({fd}<file) and closing ({fd}<&-) file descriptors
exec builtin
sysparams table
echoti builtin
SECONDS parameter
Related
I'm trying to write a script, that we call pippo.R. pippo.R aim, is to run another script (for.sh) in a for loop with a parallelization using two values :
nPerm= total number of times the script has to be run
permAtTime= number of script that can run at the same time.
A very important thing to do, is to wait for each loop to be concluded, thats why I added a file in which all the PID are stored and then I use the wait function to wait for each of them. The main problem of this script is the following error :
./wait.sh: line 2: wait: pid 836844 is not a child of this shell
For reproducibility sake you can put in a folder the following files :
pippo.R
nPerm=10
permAtTime=2
cycles=nPerm/permAtTime
for(i in 1:cycles){
d=1
system(paste("./for.sh ", i," ",permAtTime,sep=""))
}
for.sh
#!/bin/bash
for X in $(seq $1)
do
nohup ./script.sh $(($X +($2 -1)*$1 )) &
echo $! >> ./save_pid.txt
done
./wait.sh
wait.sh
#!/bin/bash
while read p; do wait $p; done < ./save_pid.txt
Running Rscript pippo.R you will have the explained error. I know that there is the parallel function that can help me in this but for several reasons i cannot use that package.
Thanks
You don't need to keep track of PIDs, because if you call wait without any argument, the script will wait for all the child processes to finish.
#!/bin/bash
for X in $(seq $1)
do
nohup ./script.sh $(($X +($2 -1)*$1 )) &
done
wait
I have a relatively simple shell script that I've plugged in as a filter to postfix. Postfix thinks it's working fine, as the log files say:
postfix/pipe[2026]: 3E2278004C: to=<me#example.com>, relay=dfilt, delay=0.12, delays=0.08/0.01/0/0.03, dsn=2.0.0, status=sent (delivered via dfilt service)
And, in fact, I get the email. However, the filter ... doesn't appear to actually be doing what I want it to do. Ultimately, this is probably a sh/bash problem, but, how do I get output from the filter somewhere where I can see it?
For example, if the filter starts
#!/bin/sh
INSPECT_DIR=/var/spool/filter
SENDMAIL=/usr/sbin/sendmail
DISCLAIMER_ADDRESSES=/etc/postfix/disclaimer_addresses
# Exit codes from <sysexits.h>
EX_TEMPFAIL=75
EX_UNAVAILABLE=69
# Clean up when done or when aborting.
# trap "rm -f in.$$" 0 1 2 3 15
# Start processing.
cd $INSPECT_DIR || { echo $INSPECT_DIR does not exist; exit
$EX_TEMPFAIL; }
cat >in.$$ || { echo Cannot save mail to file; exit $EX_TEMPFAIL; }
# obtain From address
from_address=`grep -m 1 "From:" in.$$ | cut -d "<" -f 2 | cut -d ">" -f 1`
...
How can I log whatever it's put into from_address?
OK. Turns out this was trivially easy. Postfix actually logs the output from the command, so you can just echo, or whatever your favourite debugging output is, inside of the filter script.
I have 2 files: file.dat and mapping.dat.
file.dat contains entries (can contain duplicate), and mapping.dat is a static file which contains the entries and their corresponding session/job names separated by a comma.
I have developed a simple UNIX Shell script which runs in loop where for each entry in file.dat, it searches for the session/job name in mapping.dat and displays the output.
Content of file.dat
ekm
ckm
cnc
cnx
ekm
hnm
dam
Content of mapping.dat
#Entry,Job_Name#
ckm,BDSCKM
cnc,BDSCNC
cnx,BDSCNX
ekm,BDSEKM
azm,BDSAZM
bam,BDSBAM
cam,BDSCAM
oid,BDSOID
hnm,BDSHNM
dam,BDSDAM
Current Script:
#!/bin/ksh
for FILE in `cat file.dat`
do
SESSION=`grep $FILE mapping.dat | cut -d, -f2`
echo "For ${FILE}, Session launched is: ${SESSION} "
done
Current output
For ekm, Session launched is: BDSEKM
For ckm, Session launched is: BDSCKM
For cnc, Session launched is: BDSCNC
For cnx, Session launched is: BDSCNX
For ekm, Session launched is: BDSEKM
For hnm, Session launched is: BDSHNM
For dam, Session launched is: BDSDAM
My question is I want to introduce a wait/sleep time for every 2 occurrences of output i.e. it should first display
For ekm, Session launched is: BDSEKM
For ckm, Session launched is: BDSCKM
wait for 90 seconds, and then
For cnc, Session launched is: BDSCNC
For cnx, Session launched is: BDSCNX
..and so on
Try helping yourself using the modulo operator %.
#!/bin/ksh
count=1
for file in $( cat file.dat )
do
session=$( grep $file mapping.dat | cut -d, -f2 )
echo "For ${file}, session launched is ${session}."
if (( count % 2 == 0 ))
then
sleep 90
fi
(( count++ ))
done
Here's how I would do it, I added in a message for unrecognised items (you can safely remove that by simply deleting || session='UNRECOGNIZED' from the first line of the while read loop). I'm not overly familiar with ksh, but I believe read is the same as bash in this context (I'm very familiar with bash).
I tested with your example data, and it works on both ksh and bash.
#!/bin/ksh
# Print 2 mappings every 90 seconds
FILE="./file.dat"
MAP="./mapping.dat"
while IFS= read -r line; do
session=$(grep "$line" "$MAP") || session='UNRECOGNIZED'
echo "For $line, session launched is: ${session#*,}"
((count++ % 2)) && sleep 90
done < "$FILE"
I used non greedy suffix removal (#) to isolate the 'session'.
To print the first 2 consecutive lines, then wait 90 seconds, then print the next 2 lines, etc.
Use the % modulo operator as suggested by Tony Stark. There is no need to initialize the counter.
#!/bin/bash
for file in $( cat file.dat )
do
session=$( grep $file mapping.dat | cut -d, -f2 )
echo "For ${file}, session launched is ${session}."
(( count++ % 2 )) && sleep 90
done
I have a long running process (written in Java) that I wish to run asynchronously with system(..., wait=FALSE). In order to be able to determine when the process has ended i want to create a file afterwards as per the suggestions given in How to determine when a process started with system(..., wait=FALSE) has ended. The problem is that it seems the wait parameter only applies to the last line in a multiline system command, and I can't seem to find a way around that.
Example:
system('sleep 2') # waits 2 seconds before control is returned to the user
system('sleep 2', wait=FALSE) # control is returned immediately
system('sleep 2; ls', wait=FALSE) # waits 2 seconds before control is returned to the user
I'm running on a mac system btw...
I find strange that R's system only waits for the first command (it should be calling the shell, which then waits for both commands) but using && should do it:
system('sleep 2 && ls', wait=FALSE)
If R is appending a & to the command line, it becomes sleep 2; ls & and there the & affects only the second parameter.
Another solution would be to put brackets around the commands, ( sleep 2 ; ls ) & will perform both actions sequentially:
system('( sleep 2 ; ls )', wait=FALSE)
I have a problem with a sh script.
I am using a raspberry, and want in my script to display with fbi frame buffer an image for 10 seconds. After these 10 seconds I want my script to run other sequential commands.
I wrote:
[...]
if[...]
fbi --noverbose $MEDIAFILE
MYPID=pgrep fbi
echo "[$MYPID] this is MY PID - and now i kill it!!!"
[...]
but my script stops itself in the first line (fbi --noverbose...) and I can't kill it in the next line :(.
I can't execute the command in background because I need to see the image...
any idea? thank you!
If your goal is to not show anymore after a certain amount of seconds, you can also add the command line options "-t secs" and "-1". "-t secs" is used for slideshows and is the time after which the next image is shown and "-1" means that the slideshow wont loop.
In your case:
fbi --noverbose -t 10 -1 $MEDIAFILE
This shows the image for ten seconds and then the fbi command finishes. No need to kill the process.
If fbi can't be run in the background, put your kill command in the background. To make it happen after a delay, use a subshell that sleeps first, then runs the kill command. The script would look something like this:
( sleep 10 ; kill $(pgrep fbi) ) &
fbi somefile