I noodled on Marco's answer. Code is shown below, but I will maintain this script here.
Given this crontab:
# m h dom mon dow command X=Y 1 2 3 4 5 6 echo "Hello, world" 1 2 3 4 5 6 echo "Goodby, cruel world" 1 2 3 4 5 6 echo "Please spare me the drama"
Sample usage session:
$ cronTest This is the crontab for without comment lines or blank lines: 1 X=Y 2 echo "Hello, world" 3 echo "Goodby, cruel world" 4 echo "Please spare me the drama" Which line would you like to run as now? 55 55 is not valid, please enter an integer from 1 to 4 2 Evaluating 1: X=Y Evaluating 2: echo "Hello, world" Hello, world
This is cronTest2, which needs to be properly invoked to set up the environment variables the same way as cron does:
#!/bin/bash # Prompt user for a user crontab entry to execute function deleteTempFile { rm -f $TEMP_FILE } function debug { if [ "$DEBUG" ]; then >&2 printf "$1\n"; fi } function isValidLineNumber { # $1 - number of lines # $2 - requested line number if [[ -n "${2//[0-9]+/}" ]] && (( $2 <= $1 )); then echo true; else echo false; fi } function isVariableAssignment { [[ "$( echo "$1" | grep "=" )" ]] } function makeTempCrontab { local -r ASTERISK=\\* local -r NUMBER='[[:digit:]]{1,2}' local -r NUMBERS="$NUMBER(,$NUMBER)+" local -r CRON="^(($ASTERISK|$NUMBER|$NUMBERS)[[:space:]]+)" local -r CRON5_REGEX="$CRON{5}" local -r CRON6_REGEX="$CRON{6}" rm -f "$TEMP_FILE" local -r ALL_LINES="$( crontab -l )" # Ignore empty lines and lines starting with # (comment lines) local -r LINES="$( echo "$ALL_LINES" | \ grep -v '^[[:space:]]*#' | \ grep -v '^[[:space:]]*$' )" if [[ -z "$LINES" ]]; then echo "Your crontab is empty, nothing to do" exit 1 fi IFS=$'\n' for LINE in $LINES; do LINE="$( echo "$LINE" | sed 's/\s\+$//e' )" # remove trailing space if [ "$( echo "$LINE" | grep "^$" )" ]; then debug "" # ignore empty line elif [ "$( echo "$LINE" | egrep "$CRON6_REGEX" )" ]; then debug "6 field date/time specifier: $LINE" # strip out when to run debug, leaving just the command to execute echo "$LINE" | cut -f 7- -d ' ' >> "$TEMP_FILE" elif [ "$( echo "$LINE" | egrep "$CRON5_REGEX" )" ]; then debug "5 field date/time specifier: $LINE" # strip out when to run debug, leaving just the command to execute echo "$LINE" | cut -f 6- -d ' ' >> "$TEMP_FILE" elif [ "$( echo "$LINE" | grep '^@' )" ]; then debug "@declaration: $LINE" # strip out @declaration, leaving just the command to execute echo "$LINE" | cut -f 2- -d ' ' >> "$TEMP_FILE" elif [ "$( echo "$LINE" | grep '=' )" ]; then debug "Variable assignment: $LINE" echo "$LINE" >> "$TEMP_FILE" else debug "Ignored: $LINE" fi done unset IFS } function runUpToLine { # Scans up to given line number in $TEMP_FILE # Evaluates variable assignment # Executes specified line # Ignores remainder of file # Function definitions are not supported # # $1 - line number to run readarray CONTENTS < "$TEMP_FILE" for (( i=0; i<=$1; i++ )); do # >&2 echo "\$i=$i, \$1=$1, isVariableAssignment: $( isVariableAssignment $CONTENTS[$i] ), CONTENTS[$i]=${CONTENTS[$i]}" if isVariableAssignment ${CONTENTS[$i]} || (( $i == $1 )); then printf "\nEvaluating $(( i+1 )): ${CONTENTS[$i]}" eval "${CONTENTS[$i]}" fi done } function selectLine { >&2 echo "This is the crontab for $USER without comment lines or blank lines:" cat -n "$TEMP_FILE" >&2 >&2 echo "Which line would you like to run as $USER now?" local -r NUM_LINES=$( cat "$TEMP_FILE" | wc -l ) read LINE_NUMBER # >&2 echo "NUM_LINES=$NUM_LINES, LINE_NUMBER=$LINE_NUMBER; valid: $( isValidLineNumber $NUM_LINES $LINE_NUMBER )" while [[ $( isValidLineNumber $NUM_LINES $LINE_NUMBER ) == false ]]; do >&2 echo "$LINE_NUMBER is not valid, please enter an integer from 1 to $NUM_LINES" read LINE_NUMBER # >&2 echo "NUM_LINES=$NUM_LINES, LINE_NUMBER=$LINE_NUMBER; valid: $( isValidLineNumber $NUM_LINES $LINE_NUMBER )" done (( LINE_NUMBER-- )) echo ${LINE_NUMBER} } function doIt { export USER=$1 local -r TEMP_FILE="$( mktemp crontabTest.XXX )" trap deleteTempFile EXIT makeTempCrontab local -r LINE_NUMBER="$( selectLine )" runUpToLine $LINE_NUMBER } doIt "$1"
cronTest runs cronTest2 with the proper environment variables set:
#!/bin/bash # Execute a user crontab entry with the proper environment DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" env -i bash --noprofile --norc -c "$DIR/cronTest2 $USER"