CRON job and bash script to check battery state and beep - bash

I recently changed desktop environments and the new one doesn't have a battery monitor.
I had managed to create a simple manual one by doing the following:
alias bat='upower -i /org/freedesktop/UPower/devices/battery_BAT0| grep -E "state|to\ full|percentage"'
I would like to take this to the next level and create a script that not only checks the battery level but also beeps according to it, lets say it will beep if the battery percent is less then 20%.
I would then like to run it as CRON job, which lets say will run every 5 minutes.
I also used the following to create a beep sound which I liked:
alias beep='paplay /usr/share/sounds/ubuntu/stereo/message-new-instant.ogg'
Assuming the CRON part is covered, how would you write this script in bash, assuming you use the same command as used in 'bat':
upower -i /org/freedesktop/UPower/devices/battery_BAT0
thanks

I came up with the following solution:
1) Create a bash script:
#!/bin/bash
bat='upower -i /org/freedesktop/UPower/devices/battery_BAT0| grep -E "state|to\ full|percentage"'
beep='paplay /usr/share/sounds/ubuntu/stereo/message-new-instant.ogg'
# This gets the integer percentage of current battery capacity
p=$(eval $bat | grep 'percentage' | awk '{print $2}' | awk -F '.' '{print $1}')
# if bat < 25% -> BEEP once
if [ "$p" -le 25 ] ; then
eval $beep
fi
2) Set the permissions:
chmod 700 /path/to/script.sh
3) To set a cron job run crontab -e and add the following line:
*/1 * * * * /path/to/script.sh
which means the script will be run every minute.

Related

cronjob not executing script as expected

I have this script running on a Juniper router which essentially is running Freebsd 10. Problem i am facing is the when cronjob is running this script variable ($current_mem) doesn't show any value as a result logger is also not able to log that value. Anyone have any clue what is going on.
#!/bin/bash
localpid=$$
renice -n +1 $localpid &
current_mem="test"
echo "$current_mem" <<<<shows right value
current_mem=$(cli -c "show task memory" | grep "Currently In Use" | awk
'{print $5}' | grep -o '[0-9]*')
echo "$current_mem" <<<<<<<<<<"when cron is running it show's nothing"
if [ "$current_mem" -gt "65" ]
then
echo "$current_mem" <<<<<"shows nothing"
logger -t JTASK_OS_MEM_HIGH -p "external.notice" "Using more then 65
percent of available memory Current utilization:$current_mem" <<<<<
else
echo "$current_mem"
logger -t JTASK_OS_MEM_NORMAL -p "external.notice" "Using less then 65
percent of available memory Current utilization: $current_mem"
fi
When i run this script with sh task_mem.sh, script works perfectly but when i run it through cron it doesn't show/dump the value of variable. This what i have for the cron job
# crontab -l
*/1 * * * * sh /var/tmp/task_mem.sh >>
/var/tmp/task_mem_cron.log
#GordonDavisson that was it "cli" was in /usr/sbin/cli not in /bin as a result cron was not able to execute it. Once i added the full path it started working. Thank you so much for your help

cron script won't reboot as it should

I have a Raspberry Pi connected to a VPN via openvpn. Periodically, the connection drops, so I use the following script:
#!/bin/bash
ps -ef | grep -v grep | grep openvpn
if [ $? -eq 1 ] ; then
/sbin/shutdown -r now
fi
I added it to crontab (using sudo crontab -e), I want the script to be executed every 5 minutes:
*/5 * * * * /etc/openvpn/check.sh
The script doesn't work, but it still seems to be executed every five minutes:
tail /var/log/syslog | grep CRON
gives:
Mar 16 21:15:01 raspberrypi CRON[11113]: (root) CMD (/etc/openvpn/check.sh)
...
Moreover, when I run the script manually with sudo ./check.sh, the Pi reboots just like it should.
I don't really understand what's going on here ?
Edit :
As suggested, I added the full path names and went from rebooting the Pi to restarting openvpn:
#!/bin/bash
if ! /bin/ps -ef | /bin/grep '[o]penvpn'; then
cd /etc/openvpn/
/usr/sbin/openvpn --config /etc/openvpn/config.ovpn
fi
The script still doesn't work, although it runs fine when I execute it myself. The script's permissions are 755, so it should be ok ?
The path name of the script matches the final grep so it finds itself, and is satisfied.
The reason this didn't happen interactively was that you didn't run it with a full path.
This is (a twist on) a very common FAQ.
Tangentially, your script contains two very common antipatterns. You are reinventing pidof poorly, and you are examining $? explicitly. Unless you specifically require the exit code to be 1, you should simply be doing
if ! ps -ef | grep -q '[o]penvpn'; then
because the purpose of if is to run a command and examine its exit code; and notice also the trick to use a regex which doesn't match itself. But using pidof also lets you easily examine just the binary executable's file name, not its path.
I finally understood why the script didn't work. Since it was located under /etc/openvpn, the condition if ! ps -ef | grep -q '[o]penvpn' wouldn't return true because of the script being executed. I noticed it when I changed the crontab line to:
*/5 * * * * /etc/openvpn/check.sh >/home/pi/output 2>/home/pi/erroutput
the output file showed the /etc/openvpn/check.sh script being run.
The script now is:
#!/bin/bash
if ! pidof openvpn; then
cd /etc/openvpn/
/usr/sbin/openvpn --config /etc/openvpn/config.ovpn
fi
and this works just fine. Thank you all.

Restart a process when cpu gets high

I've got a cron job checking for webserver (seeing if its active), which is handy..
http://pastebin.com/raw.php?i=KW8crfzh
I'm wanting after something similar for cpu usage. I'm running java backend which occasionally gets 70%+ cpu. I'm after a cron script to automatically kill/restart java if cpu load gets too high, how is this possible?
You could use top in batch mode coupled with some code to parse its output. For example:
top -p 1234 -n 1 -b
Will output a snapshot of the state of process 1234.
I use this script and it is pretty cool
#!/bin/bash
# author = Jaysunn
# Log
LOGFILE=/var/log/load_kill_log
# log the process causing the load at the time.
PSFILE=/var/log/ps_log
# Obtain the server load
loadavg=`uptime |cut -d , -f 4|cut -d : -f 2`
thisloadavg=`echo $loadavg|awk -F \. '{print $1}'`
if [ "$thisloadavg" -ge "10" ]; then
ps auxfww >> $PSFILE
date >> $LOGFILE
# Issue the command of choice. This can be any shell command.
## Put the command which restarts ..
fi
give executable permissions and add this to crontab with proper path to this script.

Linux crontab doesnt launch a script

I have this user crontab (accessed via the command crontab -e):
# m h dom mon dow command
*/3 * * * * sh /home/FRAPS/Desktop/cronCheck.sh
The script cronCheck.sh looks like that:
#!/bin/sh
SERVICE='Script'
if ps ax | grep -v grep | grep -i "$SERVICE" > /dev/null
then
echo "######## $SERVICE service running, everything is fine ##################\n" >> CronReport.txt
else
echo "$SERVICE is not running. Launching it now\n" >> CronReport.txt
perl Script.pl
fi
When I launch the script (cronCheck.sh) from its own directory, it works like a charm, but when cron launches it, it always "# $SERVICE service running, everything is fine ###"
despite 'Script' is not running.
Thanks,
Here's an even better way to write that conditional:
services=$(ps -e -o comm | grep -cFi "$SERVICE")
case "$services" in
(0)
# restart service
;;
(1)
# everything is fine
;;
(*)
# more than one copy is running
;;
esac
By using ps -e -o comm you avoid having to do the silly grep -v grep thing, because only the actual process name appears in the ps output, not the arguments. And grep -cFi counts up the matches and gives you a number, so you don't have to deal with the exit status of a pipeline.
Also, as other posters have implied, you should lead off this script by setting the PATH variable.
PATH=/bin:/usr/bin:/sbin:/usr/sbin
export PATH
You might or might not want to put /usr/local/bin at the beginning of that list, depending on your system. Don't do it if you don't need anything from there.
Final piece of advice: When writing scripts that will execute without user supervision (such as cron jobs), it's a good idea to put set -e at the beginning. That makes them exit unsuccessfully if any command fails.
You need to put the grep -v grep after the grep -i "$SERVICE". The way you have it now it's guaranteed to be true.
Checking the return status of a pipe like that could be problematic. You should either check the $PIPESTATUS array, or you can pipe the final grep into wc -l to count the number of lines.
cron typically does not set up a lot of the environment like a user account does. You may need to modify your script to get things setup properly.
Cron jobs don't get the same environment settings that you get at a shell prompt - those are generally set up by your shell on login - so you want to use absolute rather than relative paths throughout. (i.e. don't assume the PATH environment variable will exist or be set up the same as it is for you at a shell prompt, and don't assume the script will run with PWD set to your home directory, etc.) So:
in your crontab entry replace sh with /bin/sh (or remove it if cronCheck.sh is executable, the shebang line will do).
in cronCheck.sh add paths to the log file and the perl script.
cronCheck.sh should end up looking something like:
#!/bin/sh
SERVICE='Script'
if ps ax | grep -v grep | grep -i "$SERVICE" > /dev/null
then
echo "######## $SERVICE service running, everything is fine ##################\n" >> CronReport.txt
else
# Specify absolute path to a log file that's writeable for the user the
# cron runs as (probably you). Example: /tmp/CronReport.txt
echo "$SERVICE is not running. Launching it now\n" >> /tmp/CronReport.txt
# Specify absolute path to both perl and the script. Example: /usr/bin/perl
# and /home/FRAPS/scripts/Script.pl
/usr/bin/perl /home/FRAPS/scripts/Script.pl
fi
(Again you can get rid of the /usr/bin/perl bit if Script.pl is executable and has the path to the right perl in the shebang line.)

How to create a cron job using Bash automatically without the interactive editor?

Does crontab have an argument for creating cron jobs without using the editor (crontab -e)? If so, what would be the code to create a cron job from a Bash script?
You can add to the crontab as follows:
#write out current crontab
crontab -l > mycron
#echo new cron into cron file
echo "00 09 * * 1-5 echo hello" >> mycron
#install new cron file
crontab mycron
rm mycron
Cron line explaination
* * * * * "command to be executed"
- - - - -
| | | | |
| | | | ----- Day of week (0 - 7) (Sunday=0 or 7)
| | | ------- Month (1 - 12)
| | --------- Day of month (1 - 31)
| ----------- Hour (0 - 23)
------------- Minute (0 - 59)
Source nixCraft.
You may be able to do it on-the-fly
crontab -l | { cat; echo "0 0 0 0 0 some entry"; } | crontab -
crontab -l lists the current crontab jobs, cat prints it, echo prints the new command and crontab - adds all the printed stuff into the crontab file. You can see the effect by doing a new crontab -l.
This shorter one requires no temporary file, it is immune to multiple insertions, and it lets you change the schedule of an existing entry.
Say you have these:
croncmd="/home/me/myfunction myargs > /home/me/myfunction.log 2>&1"
cronjob="0 */15 * * * $croncmd"
To add it to the crontab, with no duplication:
( crontab -l | grep -v -F "$croncmd" ; echo "$cronjob" ) | crontab -
To remove it from the crontab whatever its current schedule:
( crontab -l | grep -v -F "$croncmd" ) | crontab -
Notes:
grep -F matches the string literally, as we do not want to interpret it as a regular expression
We also ignore the time scheduling and only look for the command. This way; the schedule can be changed without the risk of adding a new line to the crontab
Thanks everybody for your help. Piecing together what I found here and elsewhere I came up with this:
The Code
command="php $INSTALL/indefero/scripts/gitcron.php"
job="0 0 * * 0 $command"
cat <(fgrep -i -v "$command" <(crontab -l)) <(echo "$job") | crontab -
I couldn't figure out how to eliminate the need for the two variables without repeating myself.
command is obviously the command I want to schedule. job takes $command and adds the scheduling data. I needed both variables separately in the line of code that does the work.
Details
Credit to duckyflip, I use this little redirect thingy (<(*command*)) to turn the output of crontab -l into input for the fgrep command.
fgrep then filters out any matches of $command (-v option), case-insensitive (-i option).
Again, the little redirect thingy (<(*command*)) is used to turn the result back into input for the cat command.
The cat command also receives echo "$job" (self explanatory), again, through use of the redirect thingy (<(*command*)).
So the filtered output from crontab -l and the simple echo "$job", combined, are piped ('|') over to crontab - to finally be written.
And they all lived happily ever after!
In a nutshell:
This line of code filters out any cron jobs that match the command, then writes out the remaining cron jobs with the new one, effectively acting like an "add" or "update" function.
To use this, all you have to do is swap out the values for the command and job variables.
EDIT (fixed overwriting):
cat <(crontab -l) <(echo "1 2 3 4 5 scripty.sh") | crontab -
There have been a lot of good answers around the use of crontab, but no mention of a simpler method, such as using cron.
Using cron would take advantage of system files and directories located at /etc/crontab, /etc/cron.daily,weekly,hourly or /etc/cron.d/:
cat > /etc/cron.d/<job> << EOF
SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root HOME=/
01 * * * * <user> <command>
EOF
In this above example, we created a file in /etc/cron.d/, provided the environment variables for the command to execute successfully, and provided the user for the command, and the command itself. This file should not be executable and the name should only contain alpha-numeric and hyphens (more details below).
To give a thorough answer though, let's look at the differences between crontab vs cron/crond:
crontab -- maintain tables for driving cron for individual users
For those who want to run the job in the context of their user on the system, using crontab may make perfect sense.
cron -- daemon to execute scheduled commands
For those who use configuration management or want to manage jobs for other users, in which case we should use cron.
A quick excerpt from the manpages gives you a few examples of what to and not to do:
/etc/crontab and the files in /etc/cron.d must be owned by root, and must not be group- or other-writable. In contrast to the spool area, the files under /etc/cron.d or the files under /etc/cron.hourly, /etc/cron.daily, /etc/cron.weekly and /etc/cron.monthly may also be symlinks, provided that both the symlink and the file it points to are owned by root. The files under /etc/cron.d do not need to be executable, while the files under /etc/cron.hourly, /etc/cron.daily, /etc/cron.weekly and /etc/cron.monthly do, as they are run by run-parts (see run-parts(8) for more information).
Source: http://manpages.ubuntu.com/manpages/trusty/man8/cron.8.html
Managing crons in this manner is easier and more scalable from a system perspective, but will not always be the best solution.
So, in Debian, Ubuntu, and many similar Debian based distros...
There is a cron task concatenation mechanism that takes a config file, bundles them up and adds them to your cron service running.
You can put a file under the /etc/cron.d/somefilename where somefilename is whatever you want.
sudo echo "0,15,30,45 * * * * ntpdate -u time.nist.gov" >> /etc/cron.d/vmclocksync
Let's disassemble this:
sudo - because you need elevated privileges to change cron configs under the /etc directory
echo - a vehicle to create output on std out. printf, cat... would work as well
" - use a doublequote at the beginning of your string, you're a professional
0,15,30,45 * * * * - the standard cron run schedule, this one runs every 15 minutes
ntpdate -u time.nist.gov - the actual command I want to run
" - because my first double quotes needs a buddy to close the line being output
>> - the double redirect appends instead of overwrites*
/etc/cron.d/vmclocksync - vmclocksync is the filename I've chosen, it goes in /etc/cron.d/
* if we used the > redirect, we could guarantee we only had one task entry. But, we would be at risk of blowing away any other rules in an existing file. You can decide for yourself if possible destruction with > is right or possible duplicates with >> are for you. Alternatively, you could do something convoluted or involved to check if the file name exists, if there is anything in it, and whether you are adding any kind of duplicate-- but, I have stuff to do and I can't do that for you right now.
For a nice quick and dirty creation/replacement of a crontab from with a BASH script, I used this notation:
crontab <<EOF
00 09 * * 1-5 echo hello
EOF
Chances are you are automating this, and you don't want a single job added twice.
In that case use:
__cron="1 2 3 4 5 /root/bin/backup.sh"
cat <(crontab -l) |grep -v "${__cron}" <(echo "${__cron}")
This only works if you're using BASH. I'm not aware of the correct DASH (sh) syntax.
Update: This doesn't work if the user doesn't have a crontab yet. A more reliable way would be:
(crontab -l ; echo "1 2 3 4 5 /root/bin/backup.sh") | sort - | uniq - | crontab -
Alternatively, if your distro supports it, you could also use a separate file:
echo "1 2 3 4 5 /root/bin/backup.sh" |sudo tee /etc/crond.d/backup
Found those in another SO question.
echo "0 * * * * docker system prune --force >/dev/null 2>&1" | sudo tee /etc/cron.daily/dockerprune
A variant which only edits crontab if the desired string is not found there:
CMD="/sbin/modprobe fcpci"
JOB="#reboot $CMD"
TMPC="mycron"
grep "$CMD" -q <(crontab -l) || (crontab -l>"$TMPC"; echo "$JOB">>"$TMPC"; crontab "$TMPC")
(2>/dev/null crontab -l ; echo "0 3 * * * /usr/local/bin/certbot-auto renew") | crontab -
cat <(crontab -l 2>/dev/null) <(echo "0 3 * * * /usr/local/bin/certbot-auto renew") | crontab -
#write out current crontab
crontab -l > mycron 2>/dev/null
#echo new cron into cron file
echo "0 3 * * * /usr/local/bin/certbot-auto renew" >> mycron
#install new cron file
crontab mycron
rm mycron
If you're using the Vixie Cron, e.g. on most Linux distributions, you can just put a file in /etc/cron.d with the individual cronjob.
This only works for root of course. If your system supports this you should see several examples in there. (Note the username included in the line, in the same syntax as the old /etc/crontab)
It's a sad misfeature in cron that there is no way to handle this as a regular user, and that so many cron implementations have no way at all to handle this.
My preferred solution to this would be this:
(crontab -l | grep . ; echo -e "0 4 * * * myscript\n") | crontab -
This will make sure you are handling the blank new line at the bottom correctly. To avoid issues with crontab you should usually end the crontab file with a blank new line. And the script above makes sure it first removes any blank lines with the "grep ." part, and then add in a new blank line at the end with the "\n" in the end of the script. This will also prevent getting a blank line above your new command if your existing crontab file ends with a blank line.
Bash script for adding cron job without the interactive editor.
Below code helps to add a cronjob using linux files.
#!/bin/bash
cron_path=/var/spool/cron/crontabs/root
#cron job to run every 10 min.
echo "*/10 * * * * command to be executed" >> $cron_path
#cron job to run every 1 hour.
echo "0 */1 * * * command to be executed" >> $cron_path
Here is a bash function for adding a command to crontab without duplication
function addtocrontab () {
local frequency=$1
local command=$2
local job="$frequency $command"
cat <(fgrep -i -v "$command" <(crontab -l)) <(echo "$job") | crontab -
}
addtocrontab "0 0 1 * *" "echo hello"
CRON="1 2 3 4 5 /root/bin/backup.sh"
cat < (crontab -l) |grep -v "${CRON}" < (echo "${CRON}")
add -w parameter to grep exact command, without -w parameter adding the cronjob "testing" cause deletion of cron job "testing123"
script function to add/remove cronjobs. no duplication entries :
cronjob_editor () {
# usage: cronjob_editor '<interval>' '<command>' <add|remove>
if [[ -z "$1" ]] ;then printf " no interval specified\n" ;fi
if [[ -z "$2" ]] ;then printf " no command specified\n" ;fi
if [[ -z "$3" ]] ;then printf " no action specified\n" ;fi
if [[ "$3" == add ]] ;then
# add cronjob, no duplication:
( crontab -l | grep -v -F -w "$2" ; echo "$1 $2" ) | crontab -
elif [[ "$3" == remove ]] ;then
# remove cronjob:
( crontab -l | grep -v -F -w "$2" ) | crontab -
fi
}
cronjob_editor "$1" "$2" "$3"
tested :
$ ./cronjob_editor.sh '*/10 * * * *' 'echo "this is a test" > export_file' add
$ crontab -l
$ */10 * * * * echo "this is a test" > export_file
No, there is no option in crontab to modify the cron files.
You have to: take the current cron file (crontab -l > newfile), change it and put the new file in place (crontab newfile).
If you are familiar with perl, you can use this module Config::Crontab.
LLP, Andrea
script function to add cronjobs. check duplicate entries,useable expressions * > "
cronjob_creator () {
# usage: cronjob_creator '<interval>' '<command>'
if [[ -z $1 ]] ;then
printf " no interval specified\n"
elif [[ -z $2 ]] ;then
printf " no command specified\n"
else
CRONIN="/tmp/cti_tmp"
crontab -l | grep -vw "$1 $2" > "$CRONIN"
echo "$1 $2" >> $CRONIN
crontab "$CRONIN"
rm $CRONIN
fi
}
tested :
$ ./cronjob_creator.sh '*/10 * * * *' 'echo "this is a test" > export_file'
$ crontab -l
$ */10 * * * * echo "this is a test" > export_file
source : my brain ;)
Say you're logged in as the user "ubuntu", but you want to add a job to a different user's crontab, like "john", for example. You can do the following:
(sudo crontab -l -u john; echo "* * * * * command") | awk '!x[$0]++' | sudo crontab -u john -
Source for most of this solution: https://www.baeldung.com/linux/create-crontab-script
I was having tons of issues trying to add a job to another user's crontab. It kept duplicating crontabs, or just flat-out deleting them. After some testing, though, I'm confident this line of code will append a new job to a specified user's crontab, non-destructively, including not creating a job that already exists.
I wanted to find an example like this, so maybe it helps:
COMMAND="/var/lib/postgresql/backup.sh"
CRON="0 0 * * *"
USER="postgres"
CRON_FILE="postgres-backup"
# At CRON times, the USER will run the COMMAND
echo "$CRON $USER $COMMAND" | sudo tee /etc/cron.d/$CRON_FILE
echo "Cron job created. Remove /etc/cron.d/$CRON_FILE to stop it."

Resources