unixcron

How do I list all cron jobs for all users?


Is there a command or an existing script that will let me view all of a *NIX system's scheduled cron jobs at once? I'd like it to include all of the user crontabs, as well as /etc/crontab, and whatever's in /etc/cron.d. It would also be nice to see the specific commands run by run-parts in /etc/crontab.

Ideally, I'd like the output in a nice column form and ordered in some meaningful way.

I could then merge these listings from multiple servers to view the overall "schedule of events."

I was about to write such a script myself, but if someone's already gone to the trouble...


Solution

  • I ended up writing a script (I'm trying to teach myself the finer points of bash scripting, so that's why you don't see something like Perl here). It's not exactly a simple affair, but it does most of what I need. It uses Kyle's suggestion for looking up individual users' crontabs, but also deals with /etc/crontab (including the scripts launched by run-parts in /etc/cron.hourly, /etc/cron.daily, etc.) and the jobs in the /etc/cron.d directory. It takes all of those and merges them into a display something like the following:

    mi     h    d  m  w  user      command
    09,39  *    *  *  *  root      [ -d /var/lib/php5 ] && find /var/lib/php5/ -type f -cmin +$(/usr/lib/php5/maxlifetime) -print0 | xargs -r -0 rm
    47     */8  *  *  *  root      rsync -axE --delete --ignore-errors / /mirror/ >/dev/null
    17     1    *  *  *  root      /etc/cron.daily/apt
    17     1    *  *  *  root      /etc/cron.daily/aptitude
    17     1    *  *  *  root      /etc/cron.daily/find
    17     1    *  *  *  root      /etc/cron.daily/logrotate
    17     1    *  *  *  root      /etc/cron.daily/man-db
    17     1    *  *  *  root      /etc/cron.daily/ntp
    17     1    *  *  *  root      /etc/cron.daily/standard
    17     1    *  *  *  root      /etc/cron.daily/sysklogd
    27     2    *  *  7  root      /etc/cron.weekly/man-db
    27     2    *  *  7  root      /etc/cron.weekly/sysklogd
    13     3    *  *  *  archiver  /usr/local/bin/offsite-backup 2>&1
    32     3    1  *  *  root      /etc/cron.monthly/standard
    36     4    *  *  *  yukon     /home/yukon/bin/do-daily-stuff
    5      5    *  *  *  archiver  /usr/local/bin/update-logs >/dev/null
    

    Note that it shows the user, and more-or-less sorts by hour and minute so that I can see the daily schedule.

    So far, I've tested it on Ubuntu, Debian, and Red Hat AS.

    #!/bin/bash
    
    # System-wide crontab file and cron job directory. Change these for your system.
    CRONTAB='/etc/crontab'
    CRONDIR='/etc/cron.d'
    
    # Single tab character. Annoyingly necessary.
    tab=$(echo -en "\t")
    
    # Given a stream of crontab lines, exclude non-cron job lines, replace
    # whitespace characters with a single space, and remove any spaces from the
    # beginning of each line.
    function clean_cron_lines() {
        while read line ; do
            echo "${line}" |
                egrep --invert-match '^($|\s*#|\s*[[:alnum:]_]+=)' |
                sed --regexp-extended "s/\s+/ /g" |
                sed --regexp-extended "s/^ //"
        done;
    }
    
    # Given a stream of cleaned crontab lines, echo any that don't include the
    # run-parts command, and for those that do, show each job file in the run-parts
    # directory as if it were scheduled explicitly.
    function lookup_run_parts() {
        while read line ; do
            match=$(echo "${line}" | egrep -o 'run-parts (-{1,2}\S+ )*\S+')
    
            if [[ -z "${match}" ]] ; then
                echo "${line}"
            else
                cron_fields=$(echo "${line}" | cut -f1-6 -d' ')
                cron_job_dir=$(echo  "${match}" | awk '{print $NF}')
    
                if [[ -d "${cron_job_dir}" ]] ; then
                    for cron_job_file in "${cron_job_dir}"/* ; do  # */ <not a comment>
                        [[ -f "${cron_job_file}" ]] && echo "${cron_fields} ${cron_job_file}"
                    done
                fi
            fi
        done;
    }
    
    # Temporary file for crontab lines.
    temp=$(mktemp) || exit 1
    
    # Add all of the jobs from the system-wide crontab file.
    cat "${CRONTAB}" | clean_cron_lines | lookup_run_parts >"${temp}" 
    
    # Add all of the jobs from the system-wide cron directory.
    cat "${CRONDIR}"/* | clean_cron_lines >>"${temp}"  # */ <not a comment>
    
    # Add each user's crontab (if it exists). Insert the user's name between the
    # five time fields and the command.
    while read user ; do
        crontab -l -u "${user}" 2>/dev/null |
            clean_cron_lines |
            sed --regexp-extended "s/^((\S+ +){5})(.+)$/\1${user} \3/" >>"${temp}"
    done < <(cut --fields=1 --delimiter=: /etc/passwd)
    
    # Output the collected crontab lines. Replace the single spaces between the
    # fields with tab characters, sort the lines by hour and minute, insert the
    # header line, and format the results as a table.
    cat "${temp}" |
        sed --regexp-extended "s/^(\S+) +(\S+) +(\S+) +(\S+) +(\S+) +(\S+) +(.*)$/\1\t\2\t\3\t\4\t\5\t\6\t\7/" |
        sort --numeric-sort --field-separator="${tab}" --key=2,1 |
        sed "1i\mi\th\td\tm\tw\tuser\tcommand" |
        column -s"${tab}" -t
    
    rm --force "${temp}"