Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/bin/bash
- #mycrontab.bash
- #Written by Piotr Pliszka, Michael Lewis, and Henry Moore.
- #This entire snippet is meant to create a crontab in case there isn't one already present, it does so by trying to get the contents of the current crontab, and if they contain "no crontab for", it means that `crontab -l` returned an error message.
- crontab -l 2>&1 >/dev/null | grep "no crontab for" &> /dev/null
- if [ $? == 0 ]
- then
- echo No crontab present, creating...
- echo "" | crontab -
- fi
- #Sets the variable $NEWLINE to contain the newline character for pretty printing.
- printf -v NEWLINE '\n'
- #The periodicity function parses the individual cron job and ends up setting several global variables (global because bash is a mess and I couldn't figure out a less messy way of passing multiple variables worth of data around in any other way)
- #The function takes one parameter, which is the raw string containing the job to be parsed.
- #The variables set are:
- #$PERIOD_RES - Contains a human readable description of the job's periodicity
- #$JOB_RES - Contains the actual command to be executed by the job
- #$MIN - Contains the unformatted minute periodicity of the job
- #$HOUR - Contains the unformatted hour periodicity of the job
- #$DAYMONTH - Contains the unformatted day of the month periodicity of the job
- #$MONTH - Contains the unformatted month periodicity of the job
- #$DAYWEEK - Contains the unformatted day of the week periodicity of the job
- #Notes:
- #* This function does parse the special '@xxx' periodicity keywords, but the rest of the program does not cooperate with them. Assignment's description was unclear on whether this part was supposed to be handled or not. It wasn't clear on many things in general.
- #* While this function does parse both 'A-B' and 'A,B,C' syntax for each part of the periodicity, it does not parse the 'A-B/C' or other mixed syntaxes. Assignment's description was unclear on whether they were supposed to be handled or not.
- periodicity ()
- {
- #$JOB contains the raw string containing the job.
- local JOB=$1
- #This block of ifs checks for the periodicity keywords by just grepping for them.
- if echo "$JOB" | grep "@reboot" &> /dev/null ;
- then
- PERIOD_RES="Runs once after reboot."
- return
- elif echo "$JOB" | grep "@yearly" &> /dev/null ;
- then
- PERIOD_RES="Runs once a year."
- return
- elif echo "$JOB" | grep "@annually" &> /dev/null ;
- then
- PERIOD_RES="Runs once a year."
- return
- elif echo "$JOB" | grep "@monthly" &> /dev/null ;
- then
- PERIOD_RES="Runs once a month."
- return
- elif echo "$JOB" | grep "@weekly" &> /dev/null ;
- then
- PERIOD_RES="Runs once a week."
- return
- elif echo "$JOB" | grep "@daily" &> /dev/null ;
- then
- PERIOD_RES="Runs once a day."
- return
- elif echo "$JOB" | grep "@hourly" &> /dev/null ;
- then
- PERIOD_RES="Runs once an hour."
- return
- fi
- PERIOD_RES=""
- JOB_RES=""
- #$IFS is a special variable that stands for Internal Field Separator, the character(s) that bash uses to split a single string into multiple words.
- #read -a splits the input to individual words using the IFS and makes them into an array.
- IFS=' ' read -a FIELDS <<< "$JOB"
- #Since the special keywords were taken care of, we can be sure that the first five fields of the job contain the specific periodicities
- MIN="${FIELDS[0]}"
- HOUR="${FIELDS[1]}"
- DAYMONTH="${FIELDS[2]}"
- MONTH="${FIELDS[3]}"
- DAYWEEK="${FIELDS[4]}"
- #God help me this part was a nightmare
- #This entire massive block and the ones following it tries to build a human readable string that expresses the same periodicity as the cronjob syntax. Key word being 'tries'.
- #The comments in the $MIN block are applicable to all the ones following it as well with minor changes.
- PERIOD_RES="${NEWLINE}MINUTES: ${NEWLINE}"
- if [ "$MIN" = "*" ]
- then
- PERIOD_RES="${PERIOD_RES}* Runs every minute. ${NEWLINE}"
- else
- IFS=',' read -a MIN_RANGE <<< "$MIN"
- for MIN_ENTRY in "${MIN_RANGE[@]}"; do
- if echo "$MIN_ENTRY" | grep -E "\*\/" &> /dev/null ;
- then
- IFS='/' read -a MIN_RANGE2 <<< "$MIN_ENTRY"
- PERIOD_RES="${PERIOD_RES}* Runs every ${MIN_RANGE2[1]} minutes, starting at the hour.${NEWLINE}"
- elif echo "$MIN_ENTRY" | grep -E "^[0-9]+-[0-9]+$" &> /dev/null ;
- then
- IFS='-' read -a MIN_RANGE2 <<< "$MIN_ENTRY"
- PERIOD_RES="${PERIOD_RES}* Runs every minute from ${MIN_RANGE2[0]} after the hour to ${MIN_RANGE2[1]} after the hour.${NEWLINE}"
- elif echo "$MIN_ENTRY" | grep -E "^[0-9]+-[0-9]+\/[0-9]+$" &> /dev/null ;
- then
- IFS='-/' read -a MIN_RANGE2 <<< "$MIN_ENTRY"
- PERIOD_RES="${PERIOD_RES}* Runs every ${MIN_RANGE2[2]} minutes from ${MIN_RANGE2[0]} after the hour to ${MIN_RANGE2[1]} after the hour.${NEWLINE}"
- else
- PERIOD_RES="${PERIOD_RES}* Runs ${MIN_ENTRY} minutes after the hour.${NEWLINE}"
- fi
- done
- fi
- PERIOD_RES="${PERIOD_RES}HOURS: ${NEWLINE}"
- if [ "$HOUR" = "*" ]
- then
- PERIOD_RES="${PERIOD_RES}* Runs every hour. ${NEWLINE}"
- else
- IFS=',' read -a HOUR_RANGE <<< "$HOUR"
- for HOUR_ENTRY in "${HOUR_RANGE[@]}"; do
- if echo "$HOUR_ENTRY" | grep -E "\*\/" &> /dev/null ;
- then
- IFS='/' read -a HOUR_RANGE2 <<< "$HOUR_ENTRY"
- PERIOD_RES="${PERIOD_RES}* Runs every ${HOUR_RANGE2[1]} hours, starting at midnight.${NEWLINE}"
- elif echo "$HOUR_ENTRY" | grep -E "^[0-9]+-[0-9]+$" &> /dev/null ;
- then
- IFS='-' read -a HOUR_RANGE2 <<< "$HOUR_ENTRY"
- PERIOD_RES="${PERIOD_RES}* Runs every hour from ${HOUR_RANGE2[0]} to ${HOUR_RANGE2[1]}.${NEWLINE}"
- elif echo "$HOUR_ENTRY" | grep -E "^[0-9]+-[0-9]+\/[0-9]+$" &> /dev/null ;
- then
- IFS='-/' read -a HOUR_RANGE2 <<< "$HOUR_ENTRY"
- PERIOD_RES="${PERIOD_RES}* Runs every ${HOUR_RANGE2[2]} hours from ${HOUR_RANGE2[0]} to ${HOUR_RANGE2[1]}.${NEWLINE}"
- else
- PERIOD_RES="${PERIOD_RES}* Runs on ${HOUR_ENTRY} hour.${NEWLINE}"
- fi
- done
- fi
- DAYWEEKNAMES=(Sunday Monday Tuesday Wednesday Thursday Friday Saturday Sunday)
- PERIOD_RES="${PERIOD_RES}DAYS OF THE WEEK: ${NEWLINE}"
- if [ "$DAYWEEK" = "*" ]
- then
- PERIOD_RES="${PERIOD_RES}* Runs every day of the week. ${NEWLINE}"
- else
- IFS=',' read -a DAYWEEK_RANGE <<< "$DAYWEEK"
- for DAYWEEK_ENTRY in "${DAYWEEK_RANGE[@]}"; do
- if echo "$DAYWEEK_ENTRY" | grep -E "\*\/" &> /dev/null ;
- then
- IFS='/' read -a DAYWEEK_RANGE2 <<< "$DAYWEEK_ENTRY"
- PERIOD_RES="${PERIOD_RES}* Runs every ${DAYWEEK_RANGE2[1]} days, starting at Sunday.${NEWLINE}"
- elif echo "$DAYWEEK_ENTRY" | grep -E "^[0-9]+-[0-9]+$" &> /dev/null ;
- then
- IFS='-' read -a DAYWEEK_RANGE2 <<< "$DAYWEEK_ENTRY"
- DAYWEEK_0="${DAYWEEKNAMES[${DAYWEEK_RANGE2[0]}]}"
- DAYWEEK_1="${DAYWEEKNAMES[${DAYWEEK_RANGE2[1]}]}"
- PERIOD_RES="${PERIOD_RES}* Runs every day from ${DAYWEEK_0} to ${DAYWEEK_1}.${NEWLINE}"
- elif echo "$DAYWEEK_ENTRY" | grep -E "^[0-9]+-[0-9]+\/[0-9]+$" &> /dev/null ;
- then
- IFS='-/' read -a DAYWEEK_RANGE2 <<< "$DAYWEEK_ENTRY"
- DAYWEEK_0="${DAYWEEKNAMES[${DAYWEEK_RANGE2[0]}]}"
- DAYWEEK_1="${DAYWEEKNAMES[${DAYWEEK_RANGE2[1]}]}"
- PERIOD_RES="${PERIOD_RES}* Runs every ${DAYWEEK_RANGE2[2]} days from ${DAYWEEK_0} to ${DAYWEEK_1}.${NEWLINE}"
- elif echo "$DAYWEEK_ENTRY" | grep -E "^[a-zA-Z]+$" &> /dev/null ;
- then
- for DAYWEEK_NAME in "${DAYWEEKNAMES[@]}"; do
- CROP_DWEEK=${DAYWEEK_NAME::3}
- if [ "${DAYWEEK_ENTRY^^}" = "${CROP_DWEEK^^}" ]
- then
- PERIOD_RES="${PERIOD_RES}* Runs on ${DAYWEEK_NAME}.${NEWLINE}"
- fi
- done
- else
- DAYWEEK_0="${DAYWEEKNAMES[${DAYWEEK_ENTRY}]}"
- PERIOD_RES="${PERIOD_RES}* Runs on ${DAYWEEK_0}.${NEWLINE}"
- fi
- done
- fi
- PERIOD_RES="${PERIOD_RES}DAYS OF THE MONTH: ${NEWLINE}"
- if [ "$DAYMONTH" = "*" ]
- then
- PERIOD_RES="${PERIOD_RES}* Runs every day of the month. ${NEWLINE}"
- else
- IFS=',' read -a DAYMONTH_RANGE <<< "$DAYMONTH"
- for DAYMONTH_ENTRY in "${DAYMONTH_RANGE[@]}"; do
- if echo "$DAYMONTH_ENTRY" | grep -E "\*\/" &> /dev/null ;
- then
- IFS='/' read -a DAYMONTH_RANGE2 <<< "$DAYMONTH_ENTRY"
- PERIOD_RES="${PERIOD_RES}* Runs every ${DAYMONTH_RANGE2[1]} days, starting at the first of the month.${NEWLINE}"
- elif echo "$DAYMONTH_ENTRY" | grep -E "^[0-9]+-[0-9]+$" &> /dev/null ;
- then
- IFS='-' read -a DAYMONTH_RANGE2 <<< "$DAYMONTH_ENTRY"
- PERIOD_RES="${PERIOD_RES}* Runs every day from ${DAYMONTH_RANGE2[0]} to ${DAYMONTH_RANGE2[1]} of the month.${NEWLINE}"
- elif echo "$DAYMONTH_ENTRY" | grep -E "^[0-9]+-[0-9]+\/[0-9]+$" &> /dev/null ;
- then
- IFS='-/' read -a DAYMONTH_RANGE2 <<< "$DAYMONTH_ENTRY"
- PERIOD_RES="${PERIOD_RES}* Runs every ${DAYMONTH_RANGE2[2]} days from ${DAYMONTH_RANGE2[0]} to ${DAYMONTH_RANGE2[1]} of the month.${NEWLINE}"
- else
- PERIOD_RES="${PERIOD_RES}* Runs on ${DAYMONTH_ENTRY} of the month.${NEWLINE}"
- fi
- done
- fi
- MONTHNAMES=(_ January February March April May June July August September October November December)
- PERIOD_RES="${PERIOD_RES}MONTHS: ${NEWLINE}"
- if [ "$MONTH" = "*" ]
- then
- PERIOD_RES="${PERIOD_RES}* Runs every month. ${NEWLINE}"
- else
- IFS=',' read -a MONTH_RANGE <<< "$MONTH"
- for MONTH_ENTRY in "${MONTH_RANGE[@]}"; do
- if echo "$MONTH_ENTRY" | grep -E "\*\/" &> /dev/null ;
- then
- IFS='/' read -a MONTH_RANGE2 <<< "$MONTH_ENTRY"
- PERIOD_RES="${PERIOD_RES}* Runs every ${MONTH_RANGE2[1]} months, starting on January.${NEWLINE}"
- elif echo "$MONTH_ENTRY" | grep -E "^[0-9]+-[0-9]+$" &> /dev/null ;
- then
- IFS='-' read -a MONTH_RANGE2 <<< "$MONTH_ENTRY"
- MONTH_0="${MONTHNAMES[${MONTH_RANGE2[0]}]}"
- MONTH_1="${MONTHNAMES[${MONTH_RANGE2[1]}]}"
- PERIOD_RES="${PERIOD_RES}* Runs every month from ${MONTH_0} to ${MONTH_1}.${NEWLINE}"
- elif echo "$MONTH_ENTRY" | grep -E "^[0-9]+-[0-9]+\/[0-9]+$" &> /dev/null ;
- then
- IFS='-/' read -a MONTH_RANGE2 <<< "$MONTH_ENTRY"
- MONTH_0="${MONTHNAMES[${MONTH_RANGE2[0]}]}"
- MONTH_1="${MONTHNAMES[${MONTH_RANGE2[1]}]}"
- PERIOD_RES="${PERIOD_RES}* Runs every ${MONTH_RANGE2[2]} months from ${MONTH_0} to ${MONTH_1}.${NEWLINE}"
- elif echo "$MONTH_ENTRY" | grep -E "^[a-zA-Z]+$" &> /dev/null ;
- then
- for MONTH_NAME in "${MONTHNAMES[@]}"; do
- CROP_MONTH=${MONTH_NAME::3}
- if [ "${MONTH_ENTRY^^}" = "${CROP_MONTH^^}" ]
- then
- PERIOD_RES="${PERIOD_RES}* Runs on ${MONTH_NAME}.${NEWLINE}"
- fi
- done
- else
- MONTH_0="${MONTHNAMES[${MONTH_ENTRY}]}"
- PERIOD_RES="${PERIOD_RES}* Runs on ${MONTH_0}.${NEWLINE}"
- fi
- done
- fi
- #This loop just concatenates all the rest of the fields of the original job back into the command they were supposed to be.
- for ((I = 5; I < ${#FIELDS[@]}; ++I)); do
- JOB_RES="${JOB_RES}${FIELDS[$I]} "
- done
- }
- #This function acts as a basis of the 'Display Crontab Jobs' command, with the secondary purpose of setting the $JOB_NUM global variable to the number of jobs present.
- #It takes in an optional argument as to whether to run 'muted' (not display any output, just for setting the $JOB_NUM variable)
- process_cron ()
- {
- local MUTE=$1
- #We get the raw output of the `crontab -l` command this way.
- local CRONTAB=$(crontab -l)
- JOB_NUM=0
- while read -r LINE; do
- #This syntax is awkward because it's bash and I couldn't figure out how to turn it into something that doesn't hurt to read. Then again if we were meant to write something that doesn't hurt to read we wouldn't be using bash in the first place.
- FIRST_CHAR=${LINE::1}
- echo "$FIRST_CHAR" | grep "#" &> /dev/null ;
- #As to what is actually going on- the special $? variable contains the return code (not output) of the last ran command, which in this case is the grep above that checks whether a given line contains a '#' character and is therefore a comment. In grep's case, return code of anything but 0 meant that it did find something.
- #This is then ANDed with a check that the line isn't empty, which by the process of exclusion means that the line is a crontab job.
- if [ $? != "0" ] && [ "$LINE" != "" ]
- then
- #$JOB_NUM is incremented before the job is displayed because 0 based indexing is unintuitive or something.
- JOB_NUM=$((JOB_NUM+1))
- #And that's it unless the function isn't 'muted', in which case it also does the job of displaying the crontab jobs.
- if [ "$MUTE" != "1" ]
- then
- periodicity "$LINE"
- echo "${JOB_NUM}.) ${PERIOD_RES}${NEWLINE}Command to run: ${JOB_RES}${NEWLINE}"
- fi
- fi
- done <<< "$CRONTAB"
- #A fallback message in case there's nothing to list.
- if [ "${JOB_NUM}" = 0 ]
- then
- echo "No jobs to list."
- fi
- }
- #Big brother of the function above, works similarly to start with before going deep into the editing code.
- #It uses the global $EDIT_JOB variable to determine which job is to be edited. In all other cases it just copies the jobs as they are, without preserving comments or empty lines. And again, the assignment was unclear on whether comments or empty lines were to be preserved when editing the crontab.
- edit_job ()
- {
- local CRONTAB=$(crontab -l)
- JOB_NUM=0
- while read -r LINE; do
- FIRST_CHAR=${LINE::1}
- echo "$FIRST_CHAR" | grep "#" &> /dev/null ;
- if [ $? != "0" ] && [ "$LINE" != "" ]
- then
- JOB_NUM=$((JOB_NUM+1))
- if [ "$JOB_NUM" != "0" ]
- then
- EDIT_CRONTAB="${EDIT_CRONTAB}${NEWLINE}"
- fi
- #When we get to the job to edit we go into the whole new fancy menu, mimicking the main menu of the application.
- if [ "$JOB_NUM" = "$EDIT_JOB" ]
- then
- periodicity "$LINE"
- echo "${JOB_NUM}.) ${PERIOD_RES}${NEWLINE}${JOB_RES}${NEWLINE}"
- #9 is, as in the main menu, used as a 'finish'/'exit' option.
- while [ "$INPUT_EDIT" != 9 ] ; do
- echo "--------------------------"
- echo "1. Edit Minute periodicity"
- echo "2. Edit Hour periodicity"
- echo "3. Edit Day of the month periodicity"
- echo "4. Edit Month periodicity"
- echo "5. Edit Day of the week periodicity"
- echo "6. Edit Job command periodicity"
- echo "9. Finish Edit"
- echo "--------------------------"
- read -p "Select > " INPUT_EDIT </dev/tty
- #This whole parsing/reading code is copied from the `insert_job` function further down, which will contain the comments for them both.
- case $INPUT_EDIT in
- 1)
- local INSERT_MIN_OKAY=0
- while [ "$INSERT_MIN_OKAY" = "0" ] ; do
- read -p "${NEWLINE}Input the job's new minutes periodicity. Valid inputs are single numbers from 0 to 59 inclusive, comma-separated lists of numbers from 0 to 59, two numbers from 0 to 59 separated by a minus sign \"-\", or a star \"*\" meaning that the job is to be ran every minute. > " INSERT_MIN_INPUT </dev/tty
- if [ "$INSERT_MIN_INPUT" = "*" ]
- then
- INSERT_MIN_OKAY=1
- elif echo "$INSERT_MIN_INPUT" | grep -E "^[0-5]?[0-9]-[0-5]?[0-9]$" &> /dev/null ;
- then
- INSERT_MIN_OKAY=1
- elif echo "$INSERT_MIN_INPUT" | grep -E "^[0-5]?[0-9](,[0-5]?[0-9])*$" &> /dev/null ;
- then
- INSERT_MIN_OKAY=1
- else
- INSERT_MIN_OKAY=0
- echo "Error: Minutes periodicity has been specified incorrectly.${NEWLINE}${NEWLINE}"
- fi
- done
- MIN="${INSERT_MIN_INPUT}"
- ;;
- 2)
- local INSERT_HOUR_OKAY=0
- while [ "$INSERT_HOUR_OKAY" = "0" ] ; do
- read -p "${NEWLINE}Input the job's new hours periodicity. Valid inputs are single numbers from 0 to 23 inclusive, comma-separated lists of numbers from 0 to 23, two numbers from 0 to 23 separated by a minus sign \"-\", or a star \"*\" meaning that the job is to be ran every hour. > " INSERT_HOUR_INPUT </dev/tty
- if [ "$INSERT_HOUR_INPUT" = "*" ]
- then
- INSERT_HOUR_OKAY=1
- elif echo "$INSERT_HOUR_INPUT" | grep -E "^(1?[0-9]|2[0-3])-(1?[0-9]|2[0-3])$" &> /dev/null ;
- then
- INSERT_HOUR_OKAY=1
- elif echo "$INSERT_HOUR_INPUT" | grep -E "^(1?[0-9]|2[0-3])(,(1?[0-9]|2[0-3]))*$" &> /dev/null ;
- then
- INSERT_HOUR_OKAY=1
- else
- INSERT_HOUR_OKAY=0
- echo "Error: Hours periodicity has been specified incorrectly.${NEWLINE}${NEWLINE}"
- fi
- done
- HOUR="${INSERT_HOUR_INPUT}"
- ;;
- 3)
- local INSERT_DAYMONTH_OKAY=0
- while [ "$INSERT_DAYMONTH_OKAY" = "0" ] ; do
- read -p "${NEWLINE}Input the job's new days of the month periodicity. Valid inputs are single numbers from 1 to 31 inclusive, comma-separated lists of numbers from 1 to 31, two numbers from 1 to 31 separated by a minus sign \"-\", or a star \"*\" meaning that the job is to be ran every day. > " INSERT_DAYMONTH_INPUT </dev/tty
- if [ "$INSERT_DAYMONTH_INPUT" = "*" ]
- then
- INSERT_DAYMONTH_OKAY=1
- elif echo "$INSERT_DAYMONTH_INPUT" | grep -E "^([1-2]?[1-9]|[1-2]0|3[0-1])-([1-2]?[1-9]|[1-2]0|3[0-1])$" &> /dev/null ;
- then
- INSERT_DAYMONTH_OKAY=1
- elif echo "$INSERT_DAYMONTH_INPUT" | grep -E "^([1-2]?[1-9]|[1-2]0|3[0-1])(,([1-2]?[1-9]|[1-2]0|3[0-1]))*$" &> /dev/null ;
- then
- INSERT_DAYMONTH_OKAY=1
- else
- INSERT_DAYMONTH_OKAY=0
- echo "Error: Day of the month periodicity has been specified incorrectly.${NEWLINE}${NEWLINE}"
- fi
- done
- DAYMONTH="${INSERT_DAYMONTH_INPUT}"
- ;;
- 4)
- local INSERT_MONTH_OKAY=0
- while [ "$INSERT_MONTH_OKAY" = "0" ] ; do
- read -p "${NEWLINE}Input the job's new months periodicity. Valid inputs are single numbers from 1 to 12 inclusive, comma-separated lists of numbers from 1 to 12, two numbers from 1 to 12 separated by a minus sign \"-\", or a star \"*\" meaning that the job is to be ran every month. > " INSERT_MONTH_INPUT </dev/tty
- if [ "$INSERT_MONTH_INPUT" = "*" ]
- then
- INSERT_MONTH_OKAY=1
- elif echo "$INSERT_MONTH_INPUT" | grep -E "^([1-9]|1[0-2])-([1-9]|1[0-2])$" &> /dev/null ;
- then
- INSERT_MONTH_OKAY=1
- elif echo "$INSERT_MONTH_INPUT" | grep -E "^([1-9]|1[0-2])(,([1-9]|1[0-2]))*$" &> /dev/null ;
- then
- INSERT_MONTH_OKAY=1
- else
- INSERT_MONTH_OKAY=0
- echo "Error: Months periodicity has been specified incorrectly.${NEWLINE}${NEWLINE}"
- fi
- done
- MONTH="${INSERT_MONTH_INPUT}"
- ;;
- 5)
- local INSERT_DAYWEEK_OKAY=0
- while [ "$INSERT_DAYWEEK_OKAY" = "0" ] ; do
- read -p "${NEWLINE}Input the job's new days of the week periodicity. Valid inputs are single numbers from 0 to 7 inclusive, comma-separated lists of numbers from 0 to 7, two numbers from 0 to 7 separated by a minus sign \"-\", or a star \"*\" meaning that the job is to be ran every day. > " INSERT_DAYWEEK_INPUT </dev/tty
- if [ "$INSERT_DAYWEEK_INPUT" = "*" ]
- then
- INSERT_DAYWEEK_OKAY=1
- elif echo "$INSERT_DAYWEEK_INPUT" | grep -E "^[0-7]-[0-7]$" &> /dev/null ;
- then
- INSERT_DAYWEEK_OKAY=1
- elif echo "$INSERT_DAYWEEK_INPUT" | grep -E "^[0-7](,[0-7])*$" &> /dev/null ;
- then
- INSERT_DAYWEEK_OKAY=1
- else
- INSERT_DAYWEEK_OKAY=0
- echo "Error: Days of the week periodicity has been specified incorrectly.${NEWLINE}${NEWLINE}"
- fi
- done
- DAYWEEK="${INSERT_DAYWEEK_INPUT}"
- ;;
- 6)
- #There's not a real way of arbitrarily parsing whether an inputted command is valid short of just running it, so we just assume the user knows what they're doing. They probably don't but it's fine.
- read -p "Input the new job's command. > " JOB_RES
- ;;
- 9)
- #The edited job replaces the original one in the new crontab.
- EDIT_CRONTAB="${EDIT_CRONTAB}${MIN} ${HOUR} ${DAYMONTH} ${MONTH} ${DAYWEEK} ${JOB_RES}"
- ;;
- *)
- echo "Invalid command, please try again."
- ;;
- esac
- done
- else
- #In case the job isn't the one we're looking for, we just copy it verbatim.
- EDIT_CRONTAB="${EDIT_CRONTAB}${LINE}"
- fi
- fi
- #A neat syntax with while and read. I have absolutely no idea how or why it works, but I kinda doubt that our entire year combined has a combined beard long enough to even comprehend the actual explanation.
- done <<< "$CRONTAB"
- echo "${EDIT_CRONTAB}" > temp.file
- crontab temp.file
- rm temp.file
- }
- #remove_job works very similarly to edit_job above, except it just doesn't copy the job specified by $REMOVE_JOB as opposed to editing it.
- remove_job ()
- {
- local CRONTAB=$(crontab -l)
- JOB_NUM=0
- EDIT_CRONTAB=""
- while read -r LINE; do
- FIRST_CHAR=${LINE::1}
- echo "$FIRST_CHAR" | grep "#" &> /dev/null ;
- if [ $? != "0" ] && [ "$LINE" != "" ]
- then
- JOB_NUM=$((JOB_NUM+1))
- #I don't know why I wrote it this way and I dare not try to remember
- if [ "${JOB_NUM}" != "1" ]
- then
- if [ "${JOB_NUM}" != "${REMOVE_JOB}" ]
- then
- EDIT_CRONTAB="${EDIT_CRONTAB}${NEWLINE}"
- fi
- fi
- if [ "${JOB_NUM}" = "${REMOVE_JOB}" ]
- then :
- else
- EDIT_CRONTAB="${EDIT_CRONTAB}${LINE}"
- fi
- fi
- done <<< "$CRONTAB"
- echo "${EDIT_CRONTAB}" > temp.file
- crontab temp.file
- rm temp.file
- }
- #The big one with all the parsing code that is then borrowed by edit_job.
- insert_job ()
- {
- local INSERT_MIN_OKAY=0
- local INSERT_HOUR_OKAY=0
- local INSERT_DAYMONTH_OKAY=0
- local INSERT_DAYWEEK_OKAY=0
- local INSERT_MONTH_OKAY=0
- #The outline of what this function does goes like this- Each part of cronjob's periodicity has its own while loop that keeps asking for proper input until it is provided, and then it moves to the next one, until all fields are provided.
- #Proper input is either a star character '*', the 'A-B' syntax or the 'A,B,C...' syntax, latter of which also takes care of when input is just a single number.
- #Of course, all the numbers (A,B,C...) have to be valid for the given field, which is handled by regexes and grep -E.
- #All requests to convert the regex soup into actual parsing code will be interpreted as requests for a duel to the death.
- while [ "$INSERT_MIN_OKAY" = "0" ] ; do
- read -p "${NEWLINE}Input new job's minutes periodicity. Valid inputs are single numbers from 0 to 59 inclusive, comma-separated lists of numbers from 0 to 59, two numbers from 0 to 59 separated by a minus sign \"-\", or a star \"*\" meaning that the job is to be ran every minute. > " INSERT_MIN_INPUT </dev/tty
- if [ "$INSERT_MIN_INPUT" = "*" ]
- then
- INSERT_MIN_OKAY=1
- #As far as the regexes themselves go, all five blocks follow the same basic formula, where the first elif's regex is "^X-X$" and the second elif's regex is "^X(,X)*$", where X stands for the bit of regex that only matches valid numbers for a given block, so in case of minutes, numbers from 0 to 59.
- #In case of the MIN block, X is "[0-5]?[0-9]".
- #My regexes can probably all be improved, but thankfully they're not what is actually being graded.
- elif echo "$INSERT_MIN_INPUT" | grep -E "^[0-5]?[0-9]-[0-5]?[0-9]$" &> /dev/null ;
- then
- INSERT_MIN_OKAY=1
- elif echo "$INSERT_MIN_INPUT" | grep -E "^[0-5]?[0-9](,[0-5]?[0-9])*$" &> /dev/null ;
- then
- INSERT_MIN_OKAY=1
- else
- INSERT_MIN_OKAY=0
- echo "Error: Minutes periodicity has been specified incorrectly.${NEWLINE}${NEWLINE}"
- fi
- done
- while [ "$INSERT_HOUR_OKAY" = "0" ] ; do
- read -p "${NEWLINE}Input new job's hours periodicity. Valid inputs are single numbers from 0 to 23 inclusive, comma-separated lists of numbers from 0 to 23, two numbers from 0 to 23 separated by a minus sign \"-\", or a star \"*\" meaning that the job is to be ran every hour. > " INSERT_HOUR_INPUT </dev/tty
- if [ "$INSERT_HOUR_INPUT" = "*" ]
- then
- INSERT_HOUR_OKAY=1
- #In case of the HOUR block, X is "(1?[0-9]|2[0-3])".
- elif echo "$INSERT_HOUR_INPUT" | grep -E "^(1?[0-9]|2[0-3])-(1?[0-9]|2[0-3])$" &> /dev/null ;
- then
- INSERT_HOUR_OKAY=1
- elif echo "$INSERT_HOUR_INPUT" | grep -E "^(1?[0-9]|2[0-3])(,(1?[0-9]|2[0-3]))*$" &> /dev/null ;
- then
- INSERT_HOUR_OKAY=1
- else
- INSERT_HOUR_OKAY=0
- echo "Error: Hours periodicity has been specified incorrectly.${NEWLINE}${NEWLINE}"
- fi
- done
- while [ "$INSERT_DAYMONTH_OKAY" = "0" ] ; do
- read -p "${NEWLINE}Input new job's days of the month periodicity. Valid inputs are single numbers from 1 to 31 inclusive, comma-separated lists of numbers from 1 to 31, two numbers from 1 to 31 separated by a minus sign \"-\", or a star \"*\" meaning that the job is to be ran every day. > " INSERT_DAYMONTH_INPUT </dev/tty
- if [ "$INSERT_DAYMONTH_INPUT" = "*" ]
- then
- INSERT_DAYMONTH_OKAY=1
- #In case of the DAYMONTH block, X is "([1-2]?[1-9]|[1-2]0|3[0-1])"
- elif echo "$INSERT_DAYMONTH_INPUT" | grep -E "^([1-2]?[1-9]|[1-2]0|3[0-1])-([1-2]?[1-9]|[1-2]0|3[0-1])$" &> /dev/null ;
- then
- INSERT_DAYMONTH_OKAY=1
- elif echo "$INSERT_DAYMONTH_INPUT" | grep -E "^([1-2]?[1-9]|[1-2]0|3[0-1])(,([1-2]?[1-9]|[1-2]0|3[0-1]))*$" &> /dev/null ;
- then
- INSERT_DAYMONTH_OKAY=1
- else
- INSERT_DAYMONTH_OKAY=0
- echo "Error: Days of the month periodicity has been specified incorrectly.${NEWLINE}${NEWLINE}"
- fi
- done
- while [ "$INSERT_DAYWEEK_OKAY" = "0" ] ; do
- read -p "${NEWLINE}Input new job's days of the week periodicity. Valid inputs are single numbers from 0 to 7 inclusive, comma-separated lists of numbers from 0 to 7, two numbers from 0 to 7 separated by a minus sign \"-\", or a star \"*\" meaning that the job is to be ran every day. > " INSERT_DAYWEEK_INPUT </dev/tty
- if [ "$INSERT_DAYWEEK_INPUT" = "*" ]
- then
- INSERT_DAYWEEK_OKAY=1
- #In case of the DAYMONTH block, X is "[0-7]"
- elif echo "$INSERT_DAYWEEK_INPUT" | grep -E "^[0-7]-[0-7]$" &> /dev/null ;
- then
- INSERT_DAYWEEK_OKAY=1
- elif echo "$INSERT_DAYWEEK_INPUT" | grep -E "^[0-7](,[0-7])*$" &> /dev/null ;
- then
- INSERT_DAYWEEK_OKAY=1
- else
- INSERT_DAYWEEK_OKAY=0
- echo "Error: Days of the week periodicity has been specified incorrectly.${NEWLINE}${NEWLINE}"
- fi
- done
- while [ "$INSERT_MONTH_OKAY" = "0" ] ; do
- read -p "${NEWLINE}Input new job's months periodicity. Valid inputs are single numbers from 1 to 12 inclusive, comma-separated lists of numbers from 1 to 12, two numbers from 1 to 12 separated by a minus sign \"-\", or a star \"*\" meaning that the job is to be ran every month. > " INSERT_MONTH_INPUT </dev/tty
- if [ "$INSERT_MONTH_INPUT" = "*" ]
- then
- INSERT_MONTH_OKAY=1
- #In case of the DAYMONTH block, X is "([1-9]|1[0-2])"
- elif echo "$INSERT_MONTH_INPUT" | grep -E "^([1-9]|1[0-2])-([1-9]|1[0-2])$" &> /dev/null ;
- then
- INSERT_MONTH_OKAY=1
- elif echo "$INSERT_MONTH_INPUT" | grep -E "^([1-9]|1[0-2])(,([1-9]|1[0-2]))*$" &> /dev/null ;
- then
- INSERT_MONTH_OKAY=1
- else
- INSERT_MONTH_OKAY=0
- echo "Error: Months periodicity has been specified incorrectly.${NEWLINE}${NEWLINE}"
- fi
- done
- #And then the actual command is not tested at all because Halting Problem
- read -p "Input the new job's command. > " INSERT_COMMAND
- CRONTAB_DATA=$(crontab -l)
- #The new job is added by simple concatenation to the already existing crontab.
- CRONTAB_DATA="${CRONTAB_DATA}${NEWLINE}${INSERT_MIN_INPUT} ${INSERT_HOUR_INPUT} ${INSERT_DAYMONTH_INPUT} ${INSERT_MONTH_INPUT} ${INSERT_DAYWEEK_INPUT} ${INSERT_COMMAND}"
- echo "${CRONTAB_DATA}" > temp.file
- crontab temp.file
- rm temp.file
- }
- #The main loop of the program itself. Behold its splendor.
- while [ "$INPUT" != "9" ]
- do
- echo "-----------------------"
- echo "1. Display crontab jobs"
- echo "2. Insert a job"
- echo "3. Edit a job"
- echo "4. Remove a job"
- echo "5. Remove all jobs"
- echo "9. Exit"
- echo "-----------------------"
- read -p "Select > " INPUT
- case $INPUT in
- 1)
- #Lists all job with unmuted process_cron
- process_cron 0
- ;;
- 2)
- #Inserts a job with insert_job
- insert_job
- ;;
- 3)
- #First, gets the number of jobs with muted process_cron
- process_cron 1
- EDIT_INPUT_OKAY=0
- N_QUIT=0
- #Then we make sure there are actual jobs to edit. If there aren't, we display an error message and move on.
- if [ "${JOB_NUM}" != "0" ]
- then
- #What follows is a simple loop that runs until the user inserts a valid job number to edit or the character 'n', which is hardcoded to cancel editing.
- while [ "${EDIT_INPUT_OKAY}" != "1" ] ; do
- read -p "${NEWLINE}Job to edit (Type 'n' to cancel editing) > " EDIT_JOB </dev/tty
- #This regex here just parses whether the provided string is a positive integer.
- if echo "${EDIT_JOB}" | grep -E "^[0-9]+$" &> /dev/null ;
- then
- #We test if the provided job number is actually in bounds of how many jobs exist.
- if [ "${EDIT_JOB}" -gt "0" ] && [ "${EDIT_JOB}" -le "${JOB_NUM}" ]
- then
- EDIT_INPUT_OKAY=1
- else
- EDIT_INPUT_OKAY=0
- #And here we remind them of how many jobs exist just in case.
- echo "Job number outside of bounds (1-${JOB_NUM}), please try again.${NEWLINE}"
- fi
- elif [ "${EDIT_JOB}" = "n" ]
- then
- EDIT_INPUT_OKAY=1
- N_QUIT=1
- else
- EDIT_INPUT_OKAY=0
- echo "Job number must be a number, please try again.${NEWLINE}"
- fi
- done
- #$N_QUIT is a flag that specifies whether the editing was cancelled or not.
- if [ "${N_QUIT}" = "0" ]
- then
- edit_job
- fi
- else
- echo "Error: There are no jobs present to edit.${NEWLINE}"
- fi
- ;;
- 4)
- #The following block works the exact same way as the one above. Just with removing instead of editing.
- process_cron 1
- EDIT_INPUT_OKAY=0
- N_QUIT=0
- if [ "${JOB_NUM}" != "0" ]
- then
- while [ "${EDIT_INPUT_OKAY}" != "1" ] ; do
- read -p "${NEWLINE}Job to remove (Type 'n' to cancel removing) > " REMOVE_JOB </dev/tty
- if echo "${REMOVE_JOB}" | grep -E "^[0-9]+$" &> /dev/null ;
- then
- if [ "${REMOVE_JOB}" -gt "0" ] && [ "${REMOVE_JOB}" -le "${JOB_NUM}" ]
- then
- EDIT_INPUT_OKAY=1
- else
- EDIT_INPUT_OKAY=0
- echo "Job number outside of bounds (1-${JOB_NUM}), please try again.${NEWLINE}"
- fi
- elif [ "${REMOVE_JOB}" = "n" ]
- then
- EDIT_INPUT_OKAY=1
- N_QUIT=1
- else
- EDIT_INPUT_OKAY=0
- echo "Job number must be a number, please try again.${NEWLINE}"
- fi
- done
- if [ "${N_QUIT}" = "0" ]
- then
- remove_job
- fi
- else
- echo "Error: There are no jobs present to remove.${NEWLINE}"
- fi
- ;;
- 5)
- #We ask the user very nicely in case they did a goof.
- read -p "${NEWLINE}Are you sure you want to remove all jobs? ('y' to accept, anything else to cancel) > " REMOVE_JOB_ALL </dev/tty
- if [ "${REMOVE_JOB_ALL}" = "y" ] || [ "${REMOVE_JOB_ALL}" = "Y" ]
- then
- echo "" | crontab -
- fi
- ;;
- 7)
- #Debug command that just prints the contents of the crontab file.
- printf "$(crontab -l)\n"
- ;;
- 9)
- ;;
- *)
- echo "Invalid command, please try again."
- ;;
- esac
- done
- #And here it is.
- #I'm freed at last
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement