I have a deployment script.
It must add something to a user crontab
(trigger a script that cleans logs every XXX days);
however, this must be done only during the first deployment,
or when it needs to be updated.
(I can run xxx.py deploy env or xxx.py update env.)
so I need to do this:
Check if my cronJob already exists
Submit my cronJob if it does not already exist
or
update my cronJob if one of the parameter(s) of the command is different
I don’t see how to add/check/remove something to the crontab
without using crontab -e or “manually” editing the crontab file
(download it, rewrite it, re-upload it).
PS: this is a user specific cronjob;
“webadmin” is going to do it and he should not use sudo to do it.
Answers:
Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.
Method 1
My best idea so far
To check first if the content matches what should be in there and only update if it doesn’t:
if [[ $(crontab -l | egrep -v "^(#|$)" | grep -q 'some_command'; echo $?) == 1 ]]
then
set -f
echo $(crontab -l ; echo '* 1 * * * some_command') | crontab -
set +f
fi
but this gets complicated enough to build a separate script around that cron task.
Other ideas
You could send the string via stdin to crontab (beware, this clears out any previous crontab entries):
echo "* 1 * * * some_command" | crontab -
This should even work right through ssh:
echo "* 1 * * * some_command" | ssh <a href="https://getridbug.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="4732342235072f283433">[email protected]</a> "crontab -"
if you want to append to the file you could use this:
# on the machine itself echo "$(echo '* 1 * * * some_command' ; crontab -l 2>&1)" | crontab - # via ssh echo "$(echo '* 1 * * * some_command' ; ssh <a href="https://getridbug.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="483d3b2d3a0820273b3c">[email protected]</a> crontab -l 2>&1)" | ssh <a href="https://getridbug.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="4336302631032b2c3037">[email protected]</a> "crontab -"
Method 2
For the record I’m going to suggest using /etc/cron.d/. Only root can write files here but the entries can be configured to run as any user (without need for sudo at run-time). This example defines the task named my_webadmin that will execute /usr/local/bin/tidy_logfiles as the user webadmin every Sunday at midnight:
echo '0 0 * * 0 webadmin /usr/local/bin/tidy_logfiles' >/etc/cron.d/my_webadmin
An important part is that the my_webadmin should be unique to you (not necessarily unique for the run, though) because any installation package can also write files here and you want to avoid a clash. Having this uniqueness constraint, you can update my_webadmin with a simple overwrite, since you know it’s “yours” and won’t contain entries for anyone/anything else.
Furthermore, with this approach it becomes trivial to remove the cron entry
rm -f /etc/cron.d/my_webadmin
Possibly outside the scope of your question, but if you have remote access to the root account (or via sudo) you can even provision remotely,
echo '0 0 * * 0 webadmin /usr/local/bin/tidy_logfiles' > ~/webadmin.cron scp -p ~/webadmin.cron <a href="https://getridbug.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="21534e4e556153444c4e5544">[email protected]</a>_host:/etc/cron.d/my_webadmin
or,
echo '0 0 * * 0 webadmin /usr/local/bin/tidy_logfiles' |
ssh -q <a href="https://getridbug.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="9ceef3f3e8dceef9f1f3e8f9">[email protected]</a>_host 'cat >/etc/cron.d/my_webadmin'
and remove the provisioning,
ssh -nq <a href="https://getridbug.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="3a4855554e7a485f57554e5f">[email protected]</a>_host rm -f /etc/cron.d/my_webadmin
(Note that in many cases you cannot provide root’s password for the scp/ssh commands because the root account is constrained to prevent password-based logins. Instead you need to have set up public/private key certificates. Also, by implication the local account (whatever it is) will have full root access to the remote server.)
Method 3
I highly recommend using Ansible* for this rather than rolling your own. Or Puppet or Chef — but Ansible is well-suited for zero-infrastructure deploy scripts like this.
That’s because there are already modules meant to solve problems like this, and config management tools have have idempotence as a basic design goal — that’s the property of only changing when it needs to even if you accidentally (or intentionally) run it again.
In particular, Ansible’s cron module can modify user crontabs. As a bonus, if you want to later adjust to use system crontabs, it’ll be a very easy tweak rather than a rewrite.
* disclaimer: I work for Red Hat, and Ansible is a Red Hat sponsored project.
Method 4
If you want to add a cron job via the target account, run crontab -e. This command passes the crontab through an editor. Tell it to use an editor command that modifies the crontab as you desire. The editor command is executed as a shell snippet with the name of a temporary file appended.
unset VISUAL
EDITOR='update_crontab () {
set -e
new=$(mktemp)
if <"$1" grep -v "^#" | grep -w do_stuff; then
# Remove existing entries containing do_stuff
grep -v -w do_stuff "$1" >"$new"
else
cp "$1" "$new"
fi
# Add the new entry
echo "1 2 3 4 5 do_stuff --new-options" >>"$new"
mv "$new" "$1"
}
update_crontab' crontab -e
This approach is more reliable than the native crontab -l | … | crontab - because this one is vulnerable to a race condition if the crontab is edited concurrently: modifications made between the call to crontab -l and the call to crontab - would be undone.
Method 5
This is an adaptation of what @phillip-zyan-k-lee-stockmann offered, based on his “Best idea so far” code.
My changes from his (excellent and helpful snippet) are basically:
- Regex for not just the command name, but also the whole entry including time strings. That way it could support adding a command even if there are same-named or overlapping-named commands in other entries. (It still won’t add the same command on the same schedule twice.)
- A bit of logging
- I switched (and named) mine to hourly for various reasons; easy to adjust it back per crontab syntax
And so here’s my code for what I called crontab-add-hourly.sh:
#!/bin/bash
# PURPOSE:
# To allow simple, programmatic addition of commands/entries into the crontab (if not already present)
cmd=$1
entry="0 * * * * $cmd"
printf "we want to add this entry:n$entrynn"
escapedEntry=$(printf '%sn' "$entry" | sed 's:[][/.^$*]:\&:g') #from: https://unix.stackexchange.com/a/129063/320236
printf "but first we'll see if it's already in there using this regex pattern:n$escapedEntrynn"
if [[ $(crontab -l | egrep -v '^(#|$)' | grep -q "$escapedEntry"; echo $?) == 1 ]] # from: https://unix.stackexchange.com/a/297377/320236
then
printf "all clear; pattern was not already present; adding command to crontab hourly:n$cmdnn"
(crontab -l ; printf "$entrynn") | crontab -
else
printf "pattern already present; no action takennn"
fi
Example usage and output:
$ ./crontab-add-hourly.sh my-script.bash we want to add this entry: 0 * * * * my-script.bash but first we'll see if it's already in there using this regex pattern: 0 * * * * my-script.bash all clear; pattern was not already present; adding command to crontab hourly: my-script.bash
Method 6
TL;DR: This actually works, tested in Bash 4.4.
if [[ $(crontab -l | egrep -v "^(#|$)" | grep -q 'some_command'; echo $?) == 1 ]]
then
printf '%sn' "$(crontab -l; echo '* * * * * some_command')" | crontab -
fi
Method 7
echo "$(crontab -l 2>&1 ; echo '58 6 16 3 * some_command )" | crontab -
TO append to Crontab above command works best for me. It will retain old crontab jobs
Method 8
To add new job from terminal
(crontab -l 2>/dev/null; echo "25 2 * * * command/script") | crontab -
To modify existing cron job entry from terminal
(crontab -l | sed -e 's|old_part_of_job|new_part_of_job|g') | crontab -
All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0