#!/bin/bash | |
# Run as if it was called from cron, that is to say: | |
# * with a modified environment | |
# * with a specific shell, which may or may not be bash | |
# * without an attached input terminal | |
# * in a non-interactive shell | |
# This scripts supports cron jobs run by any user, just run it as the target user (e.g. using sudo -u <username>) | |
# An up-to-date version of this script may be available at https://gist.github.com/daladim/7f67eb95d59aadc8f3e8cd66c6f235d3 | |
function usage(){ | |
echo "$0 - Run a script or a command as it would be in a cron job, then display its output" | |
echo "Usage:" | |
echo " $0 [command | script]" | |
echo "" | |
echo "This scripts supports cron jobs run by any user." | |
echo "To do so, just run it as the target user (e.g. using sudo -u <username> $0)" | |
} | |
if [ "$#" -lt 1 -o "$1" == "-h" -o "$1" == "--help" ]; then | |
usage | |
exit 0 | |
fi | |
# Depending on the distro, $HOME may or may not be affected by sudo. | |
# This is a better way to get the home directory of the current user | |
home=$( getent passwd "$(whoami)" | cut -d: -f6 ) | |
cron_env="$home/.config/run-as-cron/cron-env" | |
function generate_env_file(){ | |
# This function adds /usr/bin/env to a cron job and makes sure it is run only once | |
# i.e. it just helps the user who does not want to do it himself | |
echo -n "Adding a job to cron, to be run once... " | |
generator=$(mktemp /tmp/generator.cron.XXXX) | |
chmod 700 "$generator" | |
cat > "$generator" <<eof | |
#!/bin/bash | |
mkdir -p $(dirname "$cron_env") | |
/usr/bin/env > ${cron_env} | |
# Remove this script from the current cron jobs | |
crontab -l | sed "/$(basename $generator)/d" | crontab - | |
rm "$generator" | |
eof | |
# Add the just-created-script to cron | |
crontab -l | { cat; echo "* * * * * $generator"; } | crontab - | |
if [ "$?" -eq 0 ]; then | |
echo "Done at $(date)" | |
echo "It will be run shortly. You can try to run $0 again in a minute." | |
return 0 | |
else | |
echo "Failed." | |
return 2 | |
fi | |
} | |
# This file should contain the cron environment. | |
if [ ! -f "$cron_env" ]; then | |
echo "Unable to find $cron_env" >&2 | |
echo "To generate it, run \"/usr/bin/env > $cron_env\" as a cron job" >&2 | |
echo -n "Do you want this script to do it for you? [y/n] " | |
read reply | |
if [ "$reply" == "y" -o "$reply" == "Y" ]; then | |
generate_env_file | |
exit $? | |
fi | |
exit 1 | |
fi | |
# It will be a nightmare to expand "$@" inside a shell -c argument. | |
# Let's rather generate a string where we manually expand-and-quote the arguments | |
# Note that may fail in case arguments (or environment variables) contain quotes... | |
env_string="/usr/bin/env -i " | |
while read -r line; do | |
env_string="${env_string} \"$line\" " | |
done < "$cron_env" | |
cmd_string="" | |
for arg in "$@"; do | |
cmd_string="${cmd_string} \"${arg}\" " | |
done | |
# Which shell should we use? | |
the_shell=$(grep -E "^SHELL=" "$cron_env" | sed 's/SHELL=//') | |
if [ -z "$the_shell" ]; then | |
echo "Unable to detect what shell should be used for this user." >&2 | |
exit 2 | |
fi | |
echo "Running with $the_shell the following command: $cmd_string" | |
# Let's route the output in a file | |
# and do not provide any input (so that the command is executed without an attached terminal) | |
so=$(mktemp "/tmp/fakecron.out.XXXX") | |
se=$(mktemp "/tmp/fakecron.err.XXXX") | |
"$the_shell" -c "$env_string $cmd_string" >"$so" 2>"$se" < /dev/null | |
echo -e "Done. Here is \033[1mstdout\033[0m:" | |
cat "$so" | |
echo -e "Done. Here is \033[1mstderr\033[0m:" | |
cat "$se" | |
rm "$so" "$se" |
@poleguy, thanks for your input.
I have just added a feature that bootstraps /usr/bin/env > /root/.config/run-as-cron/cron-env
, just as you suggested.
I can see that you are trying (on your own fork) to fix this script when the environment contains semicolons. Feel free to tell me when/if you successfully test it, I'd be happy to include your improvements in this script!
@poleguy, I now have included your improvements in this gist.
I also have improved a few other things, added better error management, and this script now supports any user.
Thanks for this! Updated the script: if no command is provided then crontab commands will be listed and user will be able to pick one. Available on https://gist.github.com/nbonamy/85ff67c6c1bb48120917d3935a4794aa
This would be better if it would bootstrap the /usr/bin/env > /root/cron-env the first time it runs.
It also doesn't work if cron-env has a semicolor (';') in an environment variable, e.g.:
LS_COLORS=rs=0:di=38;5;27:ln=38;5;51:mh=44;38;5;15:pi=40;38;5;11:so=38;5;13:do=38;5;5:bd=48;5;232;38;5;11:cd=48;5;232;38;5;3:or=48;5;232;38;5;9:mi=05;48;5;232;38;5;15:su=48;5;196;38;5;15:sg=48;5;11;38;5;16:ca=48;5;196;38;5;226:tw=48;5;10;38;5;16:ow=48;5;10;38;5;21:st=48;5;21;38;5;15:ex=38;5;34:.tar=38;5;9:.tgz=38;5;9:.arc=38;5;9:.arj=38;5;9:.taz=38;5;9:.lha=38;5;9:.lz4=38;5;9:.lzh=38;5;9:.lzma=38;5;9:.tlz=38;5;9:.txz=38;5;9:.tzo=38;5;9:.t7z=38;5;9:.zip=38;5;9:.z=38;5;9:.Z=38;5;9:.dz=38;5;9:.gz=38;5;9:.lrz=38;5;9:.lz=38;5;9:.lzo=38;5;9:.xz=38;5;9:.bz2=38;5;9:.bz=38;5;9:.tbz=38;5;9:.tbz2=38;5;9:.tz=38;5;9:.deb=38;5;9:.rpm=38;5;9:.jar=38;5;9:.war=38;5;9:.ear=38;5;9:.sar=38;5;9:.rar=38;5;9:.alz=38;5;9:.ace=38;5;9:.zoo=38;5;9:.cpio=38;5;9:.7z=38;5;9:.rz=38;5;9:.cab=38;5;9:.jpg=38;5;13:.jpeg=38;5;13:.gif=38;5;13:.bmp=38;5;13:.pbm=38;5;13:.pgm=38;5;13:.ppm=38;5;13:.tga=38;5;13:.xbm=38;5;13:.xpm=38;5;13:.tif=38;5;13:.tiff=38;5;13:.png=38;5;13:.svg=38;5;13:.svgz=38;5;13:.mng=38;5;13:.pcx=38;5;13:.mov=38;5;13:.mpg=38;5;13:.mpeg=38;5;13:.m2v=38;5;13:.mkv=38;5;13:.webm=38;5;13:.ogm=38;5;13:.mp4=38;5;13:.m4v=38;5;13:.mp4v=38;5;13:.vob=38;5;13:.qt=38;5;13:.nuv=38;5;13:.wmv=38;5;13:.asf=38;5;13:.rm=38;5;13:.rmvb=38;5;13:.flc=38;5;13:.avi=38;5;13:.fli=38;5;13:.flv=38;5;13:.gl=38;5;13:.dl=38;5;13:.xcf=38;5;13:.xwd=38;5;13:.yuv=38;5;13:.cgm=38;5;13:.emf=38;5;13:.axv=38;5;13:.anx=38;5;13:.ogv=38;5;13:.ogx=38;5;13:.aac=38;5;45:.au=38;5;45:.flac=38;5;45:.mid=38;5;45:.midi=38;5;45:.mka=38;5;45:.mp3=38;5;45:.mpc=38;5;45:.ogg=38;5;45:.ra=38;5;45:.wav=38;5;45:.axa=38;5;45:.oga=38;5;45:.spx=38;5;45:*.xspf=38;5;45:
This doesn'tget escaped right.
bash sucks.