Как я могу перечислить все задания cron для всех пользователей?

Есть ли команда или существующий script, который позволит мне просмотреть все заданные задачи cron в системе * NIX сразу? Я бы хотел, чтобы он включал все пользовательские crontabs, а также /etc/crontab и все в /etc/cron.d. Также было бы неплохо увидеть, что конкретные команды выполняются run-parts в /etc/crontab.

В идеале я хотел бы получить результат в хорошей форме столбца и упорядоченно каким-то значимым образом.

Затем я мог объединить эти списки с нескольких серверов, чтобы просмотреть общее "расписание событий".

Я собирался написать такой script сам, но если кто-то уже попал в беду...

Ответ 1

Я закончил писать script (я пытаюсь научить себя более тонким сценариям bash, поэтому почему вы не видите что-то вроде Perl здесь). Это не совсем простое дело, но оно делает большую часть того, что мне нужно. Он использует предложение Kyle для поиска crontab отдельных пользователей, но также имеет дело с /etc/crontab (включая скрипты, запущенные run-parts в /etc/cron.hourly, /etc/cron.daily и т.д.) И задания в каталоге /etc/cron.d. Он берет все из них и объединяет их в дисплей примерно так:

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

Обратите внимание, что он показывает пользователя и более или менее сортирует по часам и минутам, чтобы я мог видеть ежедневное расписание.

До сих пор я тестировал его на Ubuntu, Debian и 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 crontab (if it exists). Insert the user 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}"

Ответ 2

Вам нужно будет запустить это как root, но:

for user in $(cut -f1 -d: /etc/passwd); do crontab -u $user -l; done

будет зацикливаться на каждом имени пользователя, перечисляя их crontab. Кронтабы принадлежат соответствующим пользователям, поэтому вы не сможете увидеть другого пользователя crontab без его или root.


Edit если вы хотите узнать, к какому пользователю принадлежит crontab, используйте echo $user

for user in $(cut -f1 -d: /etc/passwd); do echo $user; crontab -u $user -l; done

Ответ 3

В Ubuntu или Debian вы можете просмотреть crontab в /var/spool/cron/crontabs/ а затем файл для каждого пользователя. Это только для пользовательского crontab конечно.

Для Redhat 6/7 и Centos crontab находится в /var/spool/cron/.

Ответ 4

Здесь будут показаны все записи crontab от всех пользователей.

sed 's/^\([^:]*\):.*$/crontab -u \1 -l 2>\&1/' /etc/passwd | grep -v "no crontab for" | sh

Ответ 5

Зависит от вашей версии linux, но я использую:

tail -n 1000 /var/spool/cron/*

как root. Очень простой и очень короткий.

Дает мне вывод вроде:

==> /var/spool/cron/root <==
15 2 * * * /bla

==> /var/spool/cron/my_user <==
*/10 1 * * * /path/to/script

Ответ 6

Небольшое уточнение ответа Кайла Бертона с улучшенным форматированием вывода:

#!/bin/bash
for user in $(cut -f1 -d: /etc/passwd)
do echo $user && crontab -u $user -l
echo " "
done

Ответ 7

getent passwd | cut -d: -f1 | perl -e'while(<>){chomp;$l = `crontab -u $_ -l 2>/dev/null`;print "$_\n$l\n" if $l}'

Это позволяет избежать беспорядка с помощью passwd напрямую, пропускает пользователей, у которых нет записей cron, а для тех, у кого есть они, выдает имя пользователя, а также их crontab.

В основном, отбрасывая это, хотя я могу найти его позже, если мне когда-нибудь понадобится его снова найти.

Ответ 8

Если вы проверяете кластер с использованием NIS, единственный способ проверить, есть ли у пользователя запись crontab ist в соответствии с ответом Matt/var/spool/cron/tabs.

grep -v "#" -R  /var/spool/cron/tabs

Ответ 9

Этот script работал у меня в CentOS, чтобы перечислять все crons в среде:

sudo cat /etc/passwd | sed 's/^\([^:]*\):.*$/sudo crontab -u \1 -l 2>\&1/' | grep -v "no crontab for" | sh

Ответ 10

Мне нравится простой однострочный ответ выше:

для пользователя в $(cut -f1 -d:/etc/passwd); сделать crontab -u $user -l; сделано

Но Solaris, которая не имеет флага -u и не печатает проверяемого пользователя, вы можете изменить ее так:

for user in $(cut -f1 -d: /etc/passwd); do echo User:$user; crontab -l $user 2>&1 | grep -v crontab; done

Вы получите список пользователей без ошибок, брошенных crontab, когда учетной записи не разрешено использовать cron и т.д. Имейте в виду, что в Solaris роли могут быть в /etc/passwd тоже (см./etc/user_attr).

Ответ 11

for user in $(cut -f1 -d: /etc/passwd); 
do 
    echo $user; crontab -u $user -l; 
done

Ответ 12

Следующие строки удаляют комментарии, пустые строки и ошибки от пользователей без crontab. Все, что у вас осталось, - это четкий список пользователей и их рабочие места.

Обратите внимание на использование sudo во второй строке. Если вы уже root, удалите это.

for USER in $(cut -f1 -d: /etc/passwd); do \
USERTAB="$(sudo crontab -u "$USER" -l 2>&1)";  \
FILTERED="$(echo "$USERTAB"| grep -vE '^#|^$|no crontab for|cannot use this program')";  \
if ! test -z "$FILTERED"; then  \
echo "# ------ $(tput bold)$USER$(tput sgr0) ------";  \
echo "$FILTERED";  \
echo "";  \
fi;  \
done

Пример вывода:

# ------ root ------
0 */6 * * * /usr/local/bin/disk-space-notify.sh
45 3 * * * /opt/mysql-backups/mysql-backups.sh
5 7 * * * /usr/local/bin/certbot-auto renew --quiet --no-self-upgrade

# ------ sammy ------
55 * * * * wget -O - -q -t 1 https://www.example.com/cron.php > /dev/null

Я использую это на Ubuntu (от 12 до 16) и Red Hat (от 5 до 7).

Ответ 13

Зависит от вашей версии cron. Используя Vixie cron на FreeBSD, я могу сделать что-то вроде этого:

(cd /var/cron/tabs && grep -vH ^# *) 

Если я хочу, чтобы он больше разделил табуляцию, я мог бы сделать что-то вроде этого:

(cd /var/cron/tabs && grep -vH ^# * | sed "s/:/      /")

Где эта буквальная вкладка в части замены sed.

Может быть более независимым от системы, чтобы прокрутить пользователей в /etc/passwd и сделать crontab -l -u $user для каждого из них.

Ответ 14

Спасибо за этот очень полезный script. У меня были небольшие проблемы с запуском на старых системах (Red Hat Enterprise 3, которые обрабатывают по-разному egrep и вкладки в строках), а также другие системы, в которых ничего нет в файле/etc/cron.d/(затем script с ошибкой), Итак, вот патч, чтобы заставить его работать в таких случаях:

2a3,4
> #See:  http://stackoverflow.com/questions/134906/how-do-i-list-all-cron-jobs-for-all-users
>
27c29,30
<         match=$(echo "${line}" | egrep -o 'run-parts (-{1,2}\S+ )*\S+')
---
>         #match=$(echo "${line}" | egrep -o 'run-parts (-{1,2}\S+ )*\S+')
>         match=$(echo "${line}" | egrep -o 'run-parts.*')
51c54,57
< cat "${CRONDIR}"/* | clean_cron_lines >>"${temp}"  # */ <not a comment>
---
> sys_cron_num=$(ls /etc/cron.d | wc -l | awk '{print $1}')
> if [ "$sys_cron_num" != 0 ]; then
>       cat "${CRONDIR}"/* | clean_cron_lines >>"${temp}"  # */ <not a comment>
> fi
67c73
<     sed "1i\mi\th\td\tm\tw\tuser\tcommand" |
---
>     sed "1i\mi${tab}h${tab}d${tab}m${tab}w${tab}user${tab}command" |

Я не уверен, что изменения в первом egrep - хорошая идея, но хорошо, этот script был протестирован на RHEL3,4,5 и Debian5 без каких-либо проблем. Надеюсь, это поможет!

Ответ 15

Построение поверх @Kyle

for user in $(tail -n +11 /etc/passwd | cut -f1 -d:); do echo $user; crontab -u $user -l; done

чтобы избежать комментариев, обычно в верхней части /etc/passwd,

И на macosx

for user in $(dscl . -list /users | cut -f1 -d:); do echo $user; crontab -u $user -l; done    

Ответ 16

Я думаю, что лучший лайнер будет ниже. Например, если у вас есть пользователи в NIS или LDAP, они не будут в /etc/passwd. Это даст вам crontabs каждого пользователя, который вошел в систему.

for I in `lastlog | grep -v Never | cut -f1 -d' '`; do echo $I ; crontab -l -u $I ; done

Ответ 17

вы можете написать для всего списка пользователей:

sudo crontab -u userName -l

,

Вы также можете перейти на

cd /etc/cron.daily/
ls -l
cat filename

В этом файле будут перечислены расписания

cd /etc/cron.d/
ls -l
cat filename

Ответ 18

Получить список от пользователя ROOT.

for user in $(cut -f1 -d: /etc/passwd); do echo $user; sudo crontab -u $user -l; done

Ответ 19

Поскольку речь идет о переходе через файл (/etc/passwd) и выполнении действия, у меня отсутствует правильный подход к Как я могу прочитать файл (поток данных, переменная) строка за строкой (и/или поле за полем)?:

while IFS=":" read -r user _
do
   echo "crontab for user ${user}:"
   crontab -u "$user" -l
done < /etc/passwd

Это читает /etc/passwd по строкам, используя : в качестве разделителя полей. Говоря read -r user _, мы делаем $user, удерживая первое поле, и _ остальное (это просто мусорная переменная для игнорирования полей).

Таким образом, мы можем затем вызвать crontab -u с помощью переменной $user, которую мы укажем для обеспечения безопасности (что, если оно содержит пробелы? В этом файле маловероятно, но вы никогда не узнаете).

Ответ 20

Извиняясь за yukondude, я добавил обработку для anacron в его работу, но я изменил стиль в наших домашних условиях.

Я попытался суммировать настройки времени для легкого чтения, хотя это не идеальная работа, и я не касаюсь материала "каждую пятницу".

Это версия 6 - это сейчас:

  • проверяет вызванные исполняемые файлы и флаги, отсутствующие или неисполняемые.
  • делает лучшую работу по отчетности задач анакрона.
  • проверяет столбец "user" записей, которые имеют один против пользователей на машине. Имеет побочный эффект от ловли некоторых ошибок форматирования, которые искажают количество столбцов.
  • Принимает вс... СБ дескрипторы для дней недели
  • ограничивает ширину вывода для пользовательского терминала.
#!/bin/bash

# @file      showCronJobs.sh
#
# version 6
#
# @param     Any parameter suppresses 'helpful output' and so emits only the result table.
#
# @copyright None asserted
#
# @author    DGC after yukondude
#

# @FIXME DGC 8-Jan-2019
#           This file is based on a much simpler example picked up from
#               https://stackoverflow.com/questions/134906/how-do-i-list-all-cron-jobs-for-all-users/53892185#53892185
#            authored by yukondude.
#
#           It has now evolved into a perfect example of tasks which should not be done in bash.
#
#           It uses a pile of intermediate temporary files, which we do our best to clear up on exit
#            but may still be left cluttering the place up after errors and problems.
#
#           The right way to do it is to store the info about tasks in an array of structs.
#           The bash code could be improved to use a one-dimensional array, but it can't go that far.
#
#           This really needs re-writing in a more capable language.

if [ "root" != "$(whoami)" ]
then
    echo "This script can only run as root - quitting."
    exit
fi

# Any parameter on the command line requests quiet mode.
#
quiet=false
if [ '0' != "$#" ]
then
    quiet=true
fi

#set -e
#set -x

screenColumns="$(tput cols )"
screenRows="$(   tput lines)"

if ! ${quiet}
then
    cat << EOS
WARNING - this script is clever, but not as clever as you might hope.
          It now examines the executables invoked by each cron line, and tries to warn you if they are
           not executable.
           not present where they are expected to be.
          Now there is another thing which can go wrong we we do not yet check,
           which is that the executable may be present and executable by root
           but not by the user which is scheduled to run it.
          You might hope that something on the lines of:
              if [ 'yes' = "\$(sudo -u ${user} bash -c "if [ -x ${executableAPAFN} ] ; then echo 'yes' ; else echo 'no'; fi")" ]
           would accomplish the required test. But it doesn't.
          It turns out that [ -x ] only ever tests the permission bit, and ignores the directory and file rights
           to say nothing of the fact that entire filesystems can be mounted as 'non-executable'.
          So it is not within our reasonable aspirations to spot a situation where a cron task cannot run for
           anything but the simplest reasons.
=============================================================================================================

EOS
fi

# System-wide crontab file and cron job directory. Should be standard.
#
mainCrontabAPAFN='/etc/crontab'
cronAdditionsAPADN='/etc/cron.d'

# Single tab character.
tab=$(echo -en "\t")

# Given a stream of crontab lines:
#     replace whitespace characters with a single space
#     remove any spaces from the beginning of each line.
#     exclude non-cron job lines
#     replace '@monthly' and 'Wed' type of tokens with corresponding numeric sequences
#      so they can be processed by the rest of the code.
#
# Reads from stdin, and writes to stdout.
#  SO DO NOT INSERT ECHO STATEMENTS FOR DEBUGGING HERE!!
#
# @param prefix    A string to be prepended to each line we output.
#
function cleanCronLines()
{
    prefix="$1"; shift

    # @FIXME DGC 28-Jan-2019
    #           I think we should drop all leading whitespace - this just seems to do one.

    while read line
    do
        #    sed "s/\s+/ /g"                convert all multiple-spaces to single space
        #    sed "s/^ //"                   remove any leading whitespace
        #
        #    grep
        #          --invert-match              emit only lines NOT matching the following pattern...
        #             ^(|||)                   match any of these alternatives, but only at start of line
        #             $                        blank line
        #             #                        comment       line     [ disregard leading whitespace. ]
        #             [[:alnum:]_]+=           <identifier>= line     [ disregard leading whitespace. ]
        #
        #    sed "s/^@xxxx/0 0 1 1 9/"      convert the conventional @xxx tags
        #                                    to roughly corresponding 'standard' timing settings.
        #                                    the '9' in reboot will be spotted later, and treated specially.
        #
        #    sed "s/ ... Mon /... 1/"
        #                                  convert day-of-week tokens in the 5th column into numeric equivalents
        #
        #    sed "s/ ... 0) /... 7/"
        #                                  force Sunday to be represented by 7, not zero, as it helps during sorting later.
        #
        #    insert the required prefix.
        #
        echo "${line}"                                                                                                                                   |
            sed -r "s/\s+/ /g"                                                                                                                           |
            sed -r "s/^ //"                                                                                                                              |
            grep -E -v '^$|#|[[:alnum:]_]+='                                                                                                             |
            sed -r "s/^@reboot/0 0 1 1 9/"                                                                                                               |
            sed -r "s/^@hourly/0 * * * */"                                                                                                               |
            sed -r "s/^@daily/0 0 * * */"                                                                                                                |
            sed -r "s/^@weekly/0 0 * * 0/"                                                                                                               |
            sed -r "s/^@monthly/0 0 1 * */"                                                                                                              |
            sed -r "s/^@annually/0 0 1 1 */"                                                                                                             |
            sed -r "s/^@yearly/0 0 1 1 */"                                                                                                               |
            sed -r "s/^([\*/[0-9]+|[\*/[0-9]+|\*) ([\*/[0-9]+|[\*/[0-9]+|\*) ([\*/[0-9]+|[\*/[0-9]+|\*) ([\*/[0-9]+|[\*/[0-9]+|\*) Mon /\1 \2 \3 \4 1 /" |
            sed -r "s/^([\*/[0-9]+|[\*/[0-9]+|\*) ([\*/[0-9]+|[\*/[0-9]+|\*) ([\*/[0-9]+|[\*/[0-9]+|\*) ([\*/[0-9]+|[\*/[0-9]+|\*) Tue /\1 \2 \3 \4 2 /" |
            sed -r "s/^([\*/[0-9]+|[\*/[0-9]+|\*) ([\*/[0-9]+|[\*/[0-9]+|\*) ([\*/[0-9]+|[\*/[0-9]+|\*) ([\*/[0-9]+|[\*/[0-9]+|\*) Wed /\1 \2 \3 \4 3 /" |
            sed -r "s/^([\*/[0-9]+|[\*/[0-9]+|\*) ([\*/[0-9]+|[\*/[0-9]+|\*) ([\*/[0-9]+|[\*/[0-9]+|\*) ([\*/[0-9]+|[\*/[0-9]+|\*) Thu /\1 \2 \3 \4 4 /" |
            sed -r "s/^([\*/[0-9]+|[\*/[0-9]+|\*) ([\*/[0-9]+|[\*/[0-9]+|\*) ([\*/[0-9]+|[\*/[0-9]+|\*) ([\*/[0-9]+|[\*/[0-9]+|\*) Fri /\1 \2 \3 \4 5 /" |
            sed -r "s/^([\*/[0-9]+|[\*/[0-9]+|\*) ([\*/[0-9]+|[\*/[0-9]+|\*) ([\*/[0-9]+|[\*/[0-9]+|\*) ([\*/[0-9]+|[\*/[0-9]+|\*) Sat /\1 \2 \3 \4 6 /" |
            sed -r "s/^([\*/[0-9]+|[\*/[0-9]+|\*) ([\*/[0-9]+|[\*/[0-9]+|\*) ([\*/[0-9]+|[\*/[0-9]+|\*) ([\*/[0-9]+|[\*/[0-9]+|\*) Sun /\1 \2 \3 \4 7 /" |
            sed -r "s/^([\*/[0-9]+|[\*/[0-9]+|\*) ([\*/[0-9]+|[\*/[0-9]+|\*) ([\*/[0-9]+|[\*/[0-9]+|\*) ([\*/[0-9]+|[\*/[0-9]+|\*) 0 /\1 \2 \3 \4 7 /"   |
            sed -r "s/^/${prefix} | /"
    done;
}

# Given a stream of cleaned crontab lines,
#  if they don't include the run-parts command
#      echo unchanged
#  if they do
#      show each job file in the run-parts directory as if it were scheduled explicitly.
#
# Reads from stdin, and writes to stdout.
#  SO DO NOT INSERT ECHO STATEMENTS FOR DEBUGGING HERE!!
#
function lookupRunParts()
{
    while read line
    do
        match=$(echo "${line}" | egrep -o 'run-parts (-{1,2}\S+ )*\S+' ) 
        if [ -z "${match}" ]
        then
            echo "${line}"
        else

            # This is awkward code - it needs to know how many fields there are on the line.
            # It would be better to split the line in two at the token "run-parts"
            #
            cronFields=$(echo "${line}"  | cut -f1-8 -d' '  )
            cronJobDir=$(echo "${match}" | awk '{print $NF}') 

#echo "expanding run-parts in '${line}' with cron fields '${cronFields}'"

            if [ -d "${cronJobDir}" ]
            then
                for cronJobFile in "${cronJobDir}"/*
                do 
                    if  [ -f "${cronJobFile}" ]
                    then
                        echo "${cronFields} ${cronJobFile}"
                    fi
                done
            fi
        fi
    done
}

# Temporary files for crontab lines.
#
# The following lines must match the deletion lines in the function below.
#
# A better scheme which created them in a subdirectory,
#  and set an exit trap to remove the directory and contents,
#  would be better.
#
cronLinesAPAFN="$(mktemp)"            || exit 1
cronForUserAPAFN="$(mktemp)"          || exit 1
sortedLinesAPAFN="$(mktemp)"          || exit 1
annotatedSortedLinesAPAFN="$(mktemp)" || exit 1

deleteTempFiles()
{
    # The following lines must match the four creation lines above.
    # We delete the files even if they were never created.
    #
    rm -f "${cronLinesAPAFN}"
    rm -f "${cronForUserAPAFN}"
    rm -f "${sortedLinesAPAFN}"
    rm -f "${annotatedSortedLinesAPAFN}"
}

trap deleteTempFiles EXIT

# Add all of the jobs from the main crontab file.

cat "${mainCrontabAPAFN}"    | cleanCronLines "main-crontab" | lookupRunParts >  "${cronLinesAPAFN}"

# Add all of the jobs from files in the system-wide cron.d directory.

for cronDotDFileAPAFN in "${cronAdditionsAPADN}"/*
do
    fileName="$(basename ${cronDotDFileAPAFN})"

    cat ${cronDotDFileAPAFN} | cleanCronLines "cron.d-${fileName}" | lookupRunParts >> "${cronLinesAPAFN}"
done

#echo "Main crontab and cron.d files contain:"
#cat ${cronLinesAPAFN}
#echo "-----------"
#echo

usesAnacron=false

if   cat "${cronLinesAPAFN}" | grep -qP "root.*etc/cron\.hourly/0anacron" \
  && [ -f /etc/anacrontab ]
then
    usesAnacron=true

    # Remove the line which invokes anacron hourly - that isn't a task we need to report.
    sed -i '/0anacron/d' "${cronLinesAPAFN}"

    if cat "${cronLinesAPAFN}" | grep -qP "root.*etc/cron\.*/0anacron"
    then
        echo "This script has detected that anacron has been altered to run at some other interval than 'hourly'."
        echo "It would be a simple extension of the script to handle this, but we have not written it."
        echo "Script will quit."

        exit
    fi
fi

# Get a list of users on this machine.
declare -a users
knownUsers=0

while read user
do
    users[${knownUsers}]="${user}"
    (( knownUsers++ ))

done < <(cut --fields=1 --delimiter=: /etc/passwd)

# This only works because user names cannot contain spaces.
#
# sevUsers means "semicolon-encapulated-values"
#  the list starts with, ends with, and is separated using, semicolons.
#
sevUsers=";${users[@]};"; sevUsers="${sevUsers// /;}"

# debug.
#echo "sevUsers = '${sevUsers}'"

# Examine each user crontab (if it exists). Insert the user name between the
#  five time fields and the command.

checkUser=0

while [ "${checkUser}" -lt "${knownUsers}" ]
do
    user="${users[${checkUser}]}"

    # Note that this edit will fail on a malformed line.
    #
    crontab -l -u "${user}" 2>/dev/null                             | \
        cleanCronLines  "${user}-crontab"                           | \
        sed -r "s/^(\S+) \| ((\S+ +){5})(.+)$/\1 | \2 ${user} \4/"    \
            > ${cronForUserAPAFN}

    while IFS= read -r cronLine
    do
        echo "${cronLine}"          >> "${cronLinesAPAFN}"
    done < ${cronForUserAPAFN}

    (( checkUser++ ))

done

#echo "Main crontab plus user crontabs contain:"
#cat ${cronLinesAPAFN}
#echo "-----------"
#echo

# The following section simply assumes that no-one has altered the standard /etc/anacrontab file
#  We do not completely deal with the
#      START_HOURS_RANGE
#   and
#      RANDOM_DELAY
#   parameters.
#  However we do now carry them through, and print them rather cryptically on the output.
#
# I think each task can set a further timing setting ( called 'base delay' below )
#  which we have not read up about, and completely ignore.
#
# Use of the START_HOURS_RANGE setting
#  makes the assumption that jobs run under this system are limited to 'regular housekeeping'
#  tasks which it is reasonable to suppress or put-off-till-later during certain periods of the day.
#
# That file on a server we looked at read:
#
#    # /etc/anacrontab: configuration file for anacron
#
#    # See anacron(8) and anacrontab(5) for details.
#
#    SHELL=/bin/sh
#    PATH=/sbin:/bin:/usr/sbin:/usr/bin
#    MAILTO=root
#    # the maximal random delay added to the base delay of the jobs
#    RANDOM_DELAY=45
#    # the jobs will be started during the following hours only
#    START_HOURS_RANGE=3-22
#
#    #period in days   delay in minutes   job-identifier   command
#    1                 5                  cron.daily       nice run-parts /etc/cron.daily
#    7                 25                 cron.weekly      nice run-parts /etc/cron.weekly
#    @monthly          45                 cron.monthly     nice run-parts /etc/cron.monthly
#

if ${usesAnacron}
then
    rangeSetting="$(cat /etc/anacrontab | grep "START_HOURS_RANGE" | sed 's/START_HOURS_RANGE=//')"
    delaySetting="$(cat /etc/anacrontab | grep "RANDOM_DELAY"      | sed 's/RANDOM_DELAY=//'     )"

    if [ -n "${rangeSetting}" ]
    then
        anacronPrefix="anacron_${rangeSetting}"
    else
        anacronPrefix="anacron"
    fi

    if [ -n "${delaySetting}" ]
    then
        anacronPrefix="${anacronPrefix}[~${delaySetting}]"
    fi

    # The following code inserts strings including '98',
    #  which will sort anacron tasks after non-anacron tasks
    #  in the task list we print out.
    # ( you could try using -1 to sort them 'before'. )
    #
    # We expect to spot those impossible values and replace them in the final output.

    # In a non-systemd unit, apparently anacron is only run daily.
    #  ( which of course is good enough to run it sub-tasks daily/weekly/monthly )
    # In a systemd machine it is run hourly. See:
    #     https://unix.stackexchange.com/questions/478803/is-it-true-that-cron-daily-runs-anacron-everyhour
    # However even if run hourly it does not take control of other tasks inj /etc/cron.hourly.

    # Apparently all anacron tasks run as root.

    if [ -d /etc/cron.daily ]
    then
        if find /etc/cron.daily -mindepth 1 -type f | read 
        then
            for file in $(ls /etc/cron.daily/* )
            do
                echo  "${anacronPrefix} | 98 98 * * * root ${file}"  >> "${cronLinesAPAFN}"
            done
        fi
    fi
    if [ -d /etc/cron.weekly ]
    then
        if find /etc/cron.weekly -mindepth 1 -type f | read
        then
            for file in $(ls /etc/cron.weekly/* )
            do
                echo  "${anacronPrefix} | 98 98 * * 98 root ${file}"  >> "${cronLinesAPAFN}"
            done
        fi
    fi
    if [ -d /etc/cron.monthly ]
    then
        if find /etc/cron.weekly -mindepth 1 -type f | read
        then
            for file in $(ls /etc/cron.monthly/* )
            do
                echo  "${anacronPrefix} | 98 98 1 * * root ${file}"  >> "${cronLinesAPAFN}"
            done
        fi
    fi
fi

#echo "All cron lines from all sources:"
#cat ${cronLinesAPAFN} | sed -r "s/\t/->/g"
#echo "-----------"
#echo

#
# cron lines consist of six fields with predictable formats, followed by a command line with optional embedded spaces.
#
# Output the collected crontab lines.
#  Replace the single spaces between the 6 fields with tab characters.
#  Sort the lines by hour and minute.
#  Insert the header line.
#  Format the results as a table.
#
#example:
#    root-crontab | 10 1 * * * /usr/local/sbin/hostmaker fred george

tabbedLines=$(cat "${cronLinesAPAFN}"                                                                                           | \
              sed  -r "s/^(\S+) \| (\S+) +(\S+) +(\S+) +(\S+) +(\S+) +(\S+) +(\S+) *(.*)$/\1 |\t\2\t\3\t\4\t\5\t\6\t\7\t\8 \9/"   \
             )

#echo "tabbedLines ="
#echo "${tabbedLines}"|  cut -d"${tab}" -f6  # sed -r "s/\t/->/g"
#echo "-------------------"

# Replace asteisk values with 99 - which is normally bigger than any legal value
#
# We expect to spot those impossible values and replace them in the final output
#  usually with a meaningful work like 'weekly' but failing that, back to asterisk.
#
echo "${tabbedLines}"                                 | \
   sed  -r "s/\*\t/99\t/"                             | \
   sort -t"${tab}" -k6,6n -k5,5n -k4,4n -k3,3n -k2,2n | \
   sed  -r "s/99\t/*\t/"                                \
       > ${sortedLinesAPAFN}

#echo "Sorted lines ="
#cat "${sortedLinesAPAFN}"
#echo "-------------------"

#echo "users and executables="
#cat "${sortedLinesAPAFN}" | cut -d"$tab" -f 7- | cut -d' ' -f1-2 
#echo "-------------------"

: > ${annotatedSortedLinesAPAFN}

while read sortedLine
do
    user=$(      echo -e "${sortedLine}" | cut -d"$tab" -f 7                )
    executable=$(echo -e "${sortedLine}" | cut -d"$tab" -f 8 | cut -d' ' -f1)

#debug
#    echo
#    echo "'${sevUsers}'"
#    echo "';${user};'"
#    echo

    # We will label the lines 'MALFORMED' so they shouldn't be missed,
    #  the moans here are suppressed by the 'quiet' flag.

    if [ -z "${executable}" ]
    then
        if ! ${quiet}
        then
            echo
            echo "ERROR!!!!! A cron entry is malformed - probably too few fields - making it seem to have no executable command."
            echo "The line was ( similar to ): '${sortedLine}'"
            echo
         fi

        executableTag="MALFORMED\t"

    elif [ "${user}" = "*" ]
    then
        if ! ${quiet}
        then
            echo "ERROR!!!!! A cron entry is malformed - probably too many fields - making it seem to use a user of star."
            echo "The line was ( similar to ): '${sortedLine}'"
            echo
        fi

        executableTag="MALFORMED\t"

    elif [[ ! "${sevUsers}" =~ ";${user};" ]]    # See if ;<user>;  is anywhere in the list of known users.
    then
        if ! ${quiet}
        then
            echo
            echo "ERROR!!!!! User '${user}' given in a cron entry is not a known user on this machine."
            echo "The line was ( similar to ): '${sortedLine}'"
            echo
        fi

        executableTag="MALFORMED\t"

    elif [ "/" = "${executable:0:1}" ] 
    then
        executableAPAFN="${executable}"

        if [ -f "${executableAPAFN}" ]
        then
            # See comment at the top of the file about how we would like to know if $user can execute this
            #  but testing that is too difficult to try here.
            #
            if [ -x "${executable}" ]
            then
                executableTag="executable\t"
            else
                executableTag="DISABLED   !!!\t"
            fi
        else
            executableTag="MISSING    !!!\t"
        fi
    else
        if which ${executable} > /dev/nul 2>&1
        then
            executableTag="executable\t"
        else
            executableTag="MISSING OR DISABLED !!!\t"
        fi
    fi

#    echo "${executableTag} '${sortedLine}'"

    echo "${annotatedSortedLines}${executableTag}| ${sortedLine}" >> ${annotatedSortedLinesAPAFN}

done < ${sortedLinesAPAFN}

#echo "annotatedSortedLines ="
#cat "${annotatedSortedLinesAPAFN}"
#echo "-------------------"

# We treat a repeat at any number of minutes past the hour as 'hourly' for our purposes.

# These lines convert
#     executable      | anacron_3-22[~45]   | daily at 98:98                         root        /etc/cron.daily/logrotate
#  into
#     executable      | anacron_3-22[~45]   | daily                                  root        /etc/cron.daily/logrotate
#
# It would be more sophisticated to convert them to
#     executable      | anacron             | daily < 45 mins after 03:00            root        /etc/cron.daily/logrotate

# Remove all leading zeroes on numbers
# Substitute human-radable versiosn of common numeric sequences.
#

sortedLinesTranslated=$(cat "${annotatedSortedLinesAPAFN}"                                                  |
                        sed -r "s/ 0([0-9])/ \1/"                                                           |
                        sed -r "s/\t0([0-9])\t/\t\1\t/"                                                     |
                        sed -r "s/\t0\t0\t1\t1\t9/ on reboot\t\t\t\t/"                                       |
                        sed -r "s/\t\*\/([0-9]*)\t\*\t\*\t\*\t\*/ every \1 minutes\t\t\t\t/"                 |
                        sed -r "s/\t\*\t\*\t\*\t\*\t\*/ each minute\t\t\t\t/"                                |
                        sed -r "s/\t[0-9][0-9]?\t\*\t\*\t\*\t\*/ hourly\t\t\t\t/"                            |
                        sed -r "s/\t([0-9]*)\t([0-9]*)\t\*\t\*\t\*/ daily at \2:\1\t\t\t\t/"                 |
                        sed -r "s/daily at 98:98/daily/"                                                    |
                        sed -r "s/\t0\t0\t\*\t\*\t0/ start of week\t\t\t\t/"                                 |
                        sed -r "s/\t98\t98\t\*\t\*\t0/ weekly\t\t\t\t/"                                      |
                        sed -r "s/\t0\t0\t1\t\*\t\*/ start of month\t\t\t\t/"                                |
                        sed -r "s/\t98\t98\t1\t\*\t\*/ monthly\t\t\t\t/"                                     |
                        sed -r "s/\t([0-9]*)\t([0-9]*)\t([0-9]*)\t\*\t\*/ \3 of month at \2:\1\t\t\t\t/"     |
                        sed -r "s/\t([0-9]*)\t([0-9]*)\t\*\t\*\t0*1/ Monday    at \2:\1\t\t\t\t/"            |
                        sed -r "s/\t([0-9]*)\t([0-9]*)\t\*\t\*\t0*2/ Tuesday   at \2:\1\t\t\t\t/"            |
                        sed -r "s/\t([0-9]*)\t([0-9]*)\t\*\t\*\t0*3/ Wednesday at \2:\1\t\t\t\t/"            |
                        sed -r "s/\t([0-9]*)\t([0-9]*)\t\*\t\*\t0*4/ Thursday  at \2:\1\t\t\t\t/"            |
                        sed -r "s/\t([0-9]*)\t([0-9]*)\t\*\t\*\t0*5/ Friday    at \2:\1\t\t\t\t/"            |
                        sed -r "s/\t([0-9]*)\t([0-9]*)\t\*\t\*\t0*6/ Saturday  at \2:\1\t\t\t\t/"            |
                        sed -r "s/\t([0-9]*)\t([0-9]*)\t\*\t\*\t0*7/ Sunday    at \2:\1\t\t\t\t/"            |
                        sed -r "s/\t0\t0\t1\t1\t\*/ yearly\t\t\t\t/" ) 

#echo "sortedLinesTranslated ="
#echo -e "${sortedLinesTranslated}"
#echo "-------------------"

if ! ${quiet}
then
    cat <<EOS

Common timing intervals below are converted to more readable form.

However some more obscure possible combinations
 ( such as "run at 3AM on the first Friday of each month" )
 are not handled, and will show using the original
     min  hour  day-of-month  month  day-of-week
 format"

 Anacron timings are printed in a rather cryptic code.

  executable      | anacron_3-22[~45]   | daily                        root  /etc/cron.daily/logrotate

    ( meaning tasks can only start between 03:00 and 22:00 hours,
       and will be randomly delayed up to 45 minutes ).

    If we are only using daily/weekly/monthly anacron lists.
     you would think tasks would start somewhere near the beginning of the permitted window.
     ( which is what we observe ) so it isnt clear what the 22:00 info could be useful for.
    We think it may relate to the per-anacron-task delay we currently ignore.

 should be read as:

  executable      | anacron             | daily < 45 mins after 03:00  root  /etc/cron.daily/logrotate

------------------------------ Cron tasks on this machine ------------------------------
EOS
fi

sortedLinesXlHdr="$(echo    "  \t| \t| min\thr\tdom\tmo\tdow\tuser\tcommand" ; \

                    echo -e "${sortedLinesTranslated}"                  )"

#echo "sortedLinesXlHdr ="
#echo -e "${sortedLinesXlHdr}"
#echo "-------------------"

echo ""
echo -e "${sortedLinesXlHdr}"                |
    sed 's/ | /\t| /g'                       |
    column -s"${tab}" -t                     |
    cut -c1-${screenColumns}
echo

Ответ 21

В Solaris для определенного известного имени пользователя:

crontab -l username

Всем остальным * Nix понадобится модификатор -u:

crontab -u username -l

Чтобы получить все пользовательские вакансии одновременно в Solaris, как и в других постах выше:

for user in $(cut -f1 -d: /etc/passwd); do crontab -l $user 2>/dev/null; done

Ответ 22

Для меня лучше всего смотреть/var/spool/cron/crontabs

Ответ 23

Этот script выводит Crontab в файл, а также перечисляет всех пользователей, подтверждающих те, у которых нет записи crontab:

for user in $(cut -f1 -d: /etc/passwd); do 
  echo $user >> crontab.bak
  echo "" >> crontab.bak
  crontab -u $user -l >> crontab.bak 2>> > crontab.bak
done