I have a little question and I hope someone can help me because I can not find a proper solution.
I want to resolve a hostname; while waiting for the result, I'd like to print a notification if it takes more than 30 seconds with shell script commands, preferably built-ins or ubiquitous system commands.
I have a background process that sleeps and then prints a message; while sleeping, the process runs ping, but I can't figure out how to kill the background process after the ping finishes and the message keeps printing even if the ping ends prior to the 30 second time limit since this is part of a bigger script that takes some time to run.
Here's the code that I've been using:
((sleep 30; echo "Querying the DNS server takes more than 30 seconds.") & ping -q -c 1 localhost >/dev/null)
I would greatly appreciate any and all help. Other solutions are welcome too; I just want to tell the user that the DNS is too slow and this will affect the further execution. I have tried ping -w or -W but this is not measuring the resolution time. I have tried to trap the result from the ping. I have tried to kill all processes with the same GPID but it is killing the console also. I am not the best with scripts, maybe this is the reason why this takes me so much time. Thank you in advance.
I hope this approach helps you. I think everything is pretty much portable, except for "bc" maybe. I can give you a "bc-less" version if you need it. Good luck!
#!/bin/bash
timeout=10; ## This is how long to wait before doing some batshit!
printed=1; ## this is how many times you want the message displayed (For #instance, you might want a message EVERY X seconds)
starttime="$( date +%F ) $( date +%T.%3N )"
################### HERE GOES YOUR BACKGROUND PROCESS
sleep 30 &
#######################################################
processId=$! ## And here we got the procees Id
#######################################################
while [ ! -z "$( ps -ef | grep $processId | grep -v grep )" ]
do
endtime="$( date +%F ) $( date +%T.%3N )";
timeelapsed=$( echo " $(date -d "$endtime" "+%s" ) - $(date -d "$starttime" "+%s" ) " | bc );
if [[ ($timeelapsed -gt $timeout) && ($printed -ne 0) ]]
then
echo "This is taking more than $timeout seconds";
printed=$(( printed - 1 ));
starttime="$( date +%F ) $( date +%T.%3N )"
fi
done
### Do something once everything finished
echo "The background process ended!!"
read -p 'Want to use the timer (y/n) ? ' jwb2
if [[ $jwb2 =~ ^[Yy]$ ]]; then
echo -n "Now Days "
date +"%A and hour %T"
read -p 'How many hours [24hrs] :' jwb3
echo Process the command
sudo bash -c "Shutdown -h $jwb3"
fi
and running the script now,
me#linux:$./main.sh
Want to use the timer (y/n) ? y
Now Days Thursday and hour 16:09:49
How many hours [24hrs] :18:00
Process the command
bash: Shutdown: command not found
Why am I getting this error?
Use shutdown instead Shutdown.
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
Is there any way I could run several echo statements one after the other with a delay?
For example:
The first statement will be:
echo Hello1
after 1/2 second, run the Second echo statement:
echo Hello2
Likewise, is it possible to run several statements one after the other with a time delay without printing all echoes at once?
Perhaps you would like to use sleep <number of seconds>
Like sleep 60 to wait for a minute.
eg. run from commandline
$ echo 'hello1'; sleep 2; echo 'hello2'
or in a bash script file (myscript.sh)
#!/bin/bash
echo 'hello1'
sleep 2
echo 'hello2 after 2 seconds'
sleep 2
echo 'hello3 after 2 seconds'
echo Hello1
usleep 500000 # sleep 500,000 microseconds
echo Hello2
The usleep(1) command is part of the initscripts package on Fedora.
for i in `echo "hello1 hello2 hello3"`; do echo $i; sleep 2; done
How do I write a script to determine if a file is older than 30 minutes in /bin/sh?
Unfortunately does not the stat command exist in the system. It is an old Unix system, http://en.wikipedia.org/wiki/Interactive_Unix
Perl is unfortunately not installed on the system and the customer does not want to install it, and nothing else either.
Here's one way using find.
if test "`find file -mmin +30`"
The find command must be quoted in case the file in question contains spaces or special characters.
The following gives you the file age in seconds:
echo $(( `date +%s` - `stat -L --format %Y $filename` ))
which means this should give a true/false value (1/0) for files older than 30 minutes:
echo $(( (`date +%s` - `stat -L --format %Y $filename`) > (30*60) ))
30*60 -- 60 seconds in a minute, don't precalculate, let the CPU do the work for you!
If you're writing a sh script, the most useful way is to use test with the already mentioned stat trick:
if [ `stat --format=%Y $file` -le $(( `date +%s` - 1800 )) ]; then
do stuff with your 30-minutes-old $file
fi
Note that [ is a symbolic link (or otherwise equivalent) to test; see man test, but keep in mind that test and [ are also bash builtins and thus can have slightly different behavior. (Also note the [[ bash compound command).
Ok, no stat and a crippled find. Here's your alternatives:
Compile the GNU coreutils to get a decent find (and a lot of other handy commands). You might already have it as gfind.
Maybe you can use date to get the file modification time if -r works?
(`date +%s` - `date -r $file +%s`) > (30*60)
Alternatively, use the -nt comparision to choose which file is newer, trouble is making a file with a mod time 30 minutes in the past. touch can usually do that, but all bets are off as to what's available.
touch -d '30 minutes ago' 30_minutes_ago
if [ your_file -ot 30_minutes_ago ]; then
...do stuff...
fi
And finally, see if Perl is available rather than struggling with who knows what versions of shell utilities.
use File::stat;
print "Yes" if (time - stat("yourfile")->mtime) > 60*30;
For those like myself, who don't like back ticks, based on answer by #slebetman:
echo $(( $(date +%s) - $(stat -L --format %Y $filename) > (30*60) ))
You can do this by comparing to a reference file that you've created with a timestamp of thirty minutes ago.
First create your comparison file by entering
touch -t YYYYMMDDhhmm.ss /tmp/thirty_minutes_ago
replacing the timestamp with the value thirty minutes ago. You could automate this step with a trivial one liner in Perl.
Then use find's newer operator to match files that are older by negating the search operator
find . \! -newer /tmp/thirty_minutes_ago -print
Here's my variation on find:
if [ `find cache/nodes.csv -mmin +10 | egrep '.*'` ]
Find always returns status code 0 unless it fails; however, egrep returns 1 is no match is found`. So this combination passes if that file is older than 10 minutes.
Try it:
touch /tmp/foo; sleep 61;
find /tmp/foo -mmin +1 | egrep '.*'; echo $?
find /tmp/foo -mmin +10 | egrep '.*'; echo $?
Should print 0 and then 1 after the file's path.
My function using this:
## Usage: if isFileOlderThanMinutes "$NODES_FILE_RAW" $NODES_INFO_EXPIRY; then ...
function isFileOlderThanMinutes {
if [ "" == "$1" ] ; then serr "isFileOlderThanMinutes() usage: isFileOlderThanMinutes <file> <minutes>"; exit; fi
if [ "" == "$2" ] ; then serr "isFileOlderThanMinutes() usage: isFileOlderThanMinutes <file> <minutes>"; exit; fi
## Does not exist -> "older"
if [ ! -f "$1" ] ; then return 0; fi
## The file older than $2 is found...
find "$1" -mmin +$2 | egrep '.*' > /dev/null 2>&1;
if [ $? == 0 ] ; then return 0; fi ## So it is older.
return 1; ## Else it not older.
}
Difference in seconds between current time and last modification time of myfile.txt:
echo $(($(date +%s)-$(stat -c "%Y" myfile.txt)))
you can also use %X or %Z with the command stat -c to get the difference between last access or last status change, check for 0 return!
%X time of last access, seconds since Epoch
%Y time of last data modification, seconds since Epoch
%Z time of last status change, seconds since Epoch
The test:
if [ $(($(date +%s)-$(stat -c "%Y" myfile.txt))) -lt 600 ] ; then echo younger than 600 sec ; else echo older than 600 sec ; fi
What do you mean by older than 30 minutes: modified more than 30 minutes ago, or created more than 30 minutes ago? Hopefully it's the former, as the answers so far are correct for that interpretation. In the latter case, you have problems since unix file systems do not track the creation time of a file. (The ctime file attribute records when the inode contents last changed, ie, something like chmod or chown happened).
If you really need to know if file was created more than 30 minutes ago, you'll either have to scan the relevant part of the file system repeatedly with something like find or use something platform-dependent like linux's inotify.
#!/usr/bin/ksh
## this script creates a new timer file every minute and renames all the previously created timer files and then executes whatever script you need which can now use the timer files to compare against with a find. The script is designed to always be running on the server. The first time the script is executed it will remove the timer files and it will take an hour to rebuild them (assuming you want 60 minutes of timer files)
set -x
# if the server is rebooted for any reason or this scripts stops we must rebuild the timer files from scratch
find /yourpath/timer -type f -exec rm {} \;
while [ 1 ]
do
COUNTER=60
COUNTER2=60
cd /yourpath/timer
while [ COUNTER -gt 1 ]
do
COUNTER2=`expr $COUNTER - 1`
echo COUNTER=$COUNTER
echo COUNTER2=$COUNTER2
if [ -f timer-minutes-$COUNTER2 ]
then
mv timer-minutes-$COUNTER2 timer-minutes-$COUNTER
COUNTER=`expr $COUNTER - 1`
else
touch timer-minutes-$COUNTER2
fi
done
touch timer-minutes-1
sleep 60
#this will check to see if the files have been fully updated after a server restart
COUNT=`find . ! -newer timer-minutes-30 -type f | wc -l | awk '{print $1}'`
if [ $COUNT -eq 1 ]
then
# execute whatever scripts at this point
fi
done
You can use the find command.
For example, to search for files in current dir that are older than 30 min:
find . -type f -mmin +30
You can read up about the find command HERE
if [[ "$(date --rfc-3339=ns -r /tmp/targetFile)" < "$(date --rfc-3339=ns --date '90 minutes ago')" ]] ; then echo "older"; fi