1

I have a rather large BASH function that I'm working on. This function is a CronJob generator. This script is intended to be run with SUDO privileges, and will allow the user to inspect the unprivileged user's Crontab file. They can create new cronjobs (a few questions and it does the proper syntax for them), they can also remove a cronjob. That's where I've hit a wall.

In this part of my CASE statement, the user has been asked if they want to create a new cronjob -- they reply with "N" or "n" and we get here:

#!/bin/bash
read -r -p $'Would you like to create a new cronjob? [y/n]\n\n--> ' CRON
case "$CRON" in
    y|Y)
        echo "not pertinent to this discussion"
        ;;
    n|N)
        read -r -p $'\n\nWould you like to REMOVE a crontab entry? [y/n]: ' REMOVE
        case "$REMOVE" in
            Y|y)
                declare -a CRONTAB
                while IFS= read -r LINE
                    do
                        CRONTAB+=("$LINE")
                    done < <(grep -v '#' /var/spool/cron/"$SCRIPTUSER")
                echo -en "\nPlease select a cronjob to remove from the Crontab file:\n\n"
                PS3=$'\n\nPlease enter your selection: '
                select LINE in "${CRONTAB[@]}"
                    do
                        echo "Going to remove \"$LINE\""
                        read -r -p $'Is this correct? [y/n]' CHOICE
                        case "$CHOICE" in
                            Y|y)
                                sed "s/$LINE/^#&/g" -i /var/spool/cron/"$SCRIPTUSER"
                                break
                                ;;
                            N|n)
                                break
                                ;;
                        esac
                    done
                echo -en "\n\nCurrent Crontab entries for $SCRIPTUSER:\n\n"
                echo -en "\n\n######################################\n\n$(grep -v '#' /var/spool/cron/"$SCRIPTUSER")\n\n######################################\n\n"
                sleep 3
                break
                ;;
            N|n)
                break
                ;;
        esac
        ;;
esac

The problem I'm having is these are my cronjob entries I'm testing with:

Image of two standard Linux crontab entries

The SED statement doesn't seem to be doing anything at all. I would imagine the '*' and '/' are probably messing with the SED pattern, and I have already tried a sed where I escaped all the '/' but it still passed over it like nothing was there.

I appreciate the extra set of eyeballs, thank you!

misteralexander
  • 448
  • 2
  • 7
  • 19
  • `sed` is getting tripped up by the `/` chars in your `/bin/logger`. Use `sed 's@srch@repl@`` or some other char that will not be in your data stream. Good luck. – shellter Dec 07 '19 at 19:46
  • 1
    Alternatively, instead of matching the whole line and trying some escaping contortions, you could collect the line numbers of the uncommented cron jobs and then use that line number in the deletion command, something like `awk '!/^ *#/ { print NR, $0 }'`, and then use the line number as in `sed -i "${line}d"`. – Benjamin W. Dec 07 '19 at 21:16
  • Also, `sed` will parse the search pattern (`$LINE`) as a regular expression, which probably will not match itself (for example, `35 16 * * 5` will match "35 16" followed by one or more spaces (but no asterisks) followed by "5". See "Escaping a string literal for use as a regex in `sed`" in [this answer](https://stackoverflow.com/questions/29613304/is-it-possible-to-escape-regex-metacharacters-reliably-with-sed/29613573#29613573) (or use the line number as @BenjaminW. suggested). – Gordon Davisson Dec 07 '19 at 21:18
  • You might also look into Ansible's [cron](https://docs.ansible.com/ansible/latest/modules/cron_module.html) as an alternative. – Benjamin W. Dec 07 '19 at 21:25
  • @BenjaminW. how do I take the line numbers from the awk expression and feed that into a select -able menu for a user to pick from? – misteralexander Dec 07 '19 at 22:02
  • 1
    Your `CRONTAB` array would contain elements of the form `X `, where `X` is the line number; when an entry is selected, extract the line number with something like `read -r lineno _ <<< "$LINE"` and then use `sed -i "${lineno}d"` – Benjamin W. Dec 07 '19 at 22:23
  • That would make the menu items include the line number, though, which might be a little confusing. – Benjamin W. Dec 07 '19 at 22:24
  • 1
    FWIW `crontab -e` works for any user, you don't need to implement it yourself. – root Dec 08 '19 at 07:30
  • @BenjaminW. Please post your two solutions as the answer, so you get credit for them. They worked perfectly! Thank you!!! – misteralexander Dec 09 '19 at 20:09

0 Answers0