Проверьте, запускается ли скрипт cron, а не вызывается вручную


23

Есть ли переменная, которую устанавливает cron при запуске программы? Если скрипт запускается cron, я бы хотел пропустить некоторые части; в противном случае вызовите эти части.

Как я могу узнать, запущен ли скрипт Bash cron?


Почему ты не только мы ps?
Тердон


@terdon: вероятно потому, что psон довольно плохо документирован (особенно версия Linux, которая поддерживает несколько различных синтаксических стилей), а страница руководства даже более плотная и загадочная, чем большинство инструментов. Я подозреваю, что большинство людей даже не осознают, насколько полезным и универсальным psможет быть инструмент .
Cas

Ответы:


31

Я не знаю, cronчто по умолчанию делает что-то для его среды, что может быть здесь полезно, но есть несколько вещей, которые вы могли бы сделать, чтобы получить желаемый эффект.

1) Сделать жесткую или программную ссылку на файл скрипта, чтобы, например, myscriptи myscript_via_cronуказать на тот же файл. Затем вы можете проверить значение $0внутри скрипта, если хотите условно запустить или пропустить определенные части кода. Поместите подходящее имя в ваш crontab, и все готово.

2) Добавьте параметр в сценарий и установите этот параметр в вызове crontab. Например, добавьте параметр -c, который указывает сценарию запускать или пропустить соответствующие части кода, и добавьте -cимя команды в ваш crontab.

И, конечно же, cron может устанавливать произвольные переменные окружения, поэтому вы можете просто вставить строку, как RUN_BY_CRON="TRUE"в вашем crontab, и проверить ее значение в вашем скрипте.


7
+1 для RUN_BY_CRON = True
саз

Ответ от cas работает очень хорошо и может быть использован для чего угодно еще
Deian

19

Скрипты, запускаемые из cron, не запускаются в интерактивных оболочках. Ни один из сценариев запуска. Различие состоит в том, что к интерактивным оболочкам подключены STDIN и STDOUT к tty.

Способ 1: проверьте, $-включает ли iфлаг. iустанавливается для интерактивных оболочек.

case "$-" in
    *i*)
        interactive=1
        ;;
    *)
        not_interactive=1
        ;;
esac

Способ 2: проверка $PS1пуста.

if [ -z "$PS1" ]; then
    not_interactive=1 
else
    interactive=1
fi

ссылка: http://techdoc.kvindesland.no/linux/gnubooks/bash/bashref_54.html

Способ 3: проверить свой tty. это не так надежно, но с простыми заданиями cron вы должны быть в порядке, так как cron по умолчанию не выделяет tty для скрипта.

if [ -t 0 ]; then
    interactive=1
else
    non_interactive=1
fi

Имейте в виду, что вы можете принудительно использовать интерактивную оболочку -i, но вы, вероятно, будете знать, если бы вы делали это ...


1
Обратите внимание, что команда $ PS1 не работает при проверке запуска сценария systemd или нет. $ - один делает
mveroone

1
Ваша ссылка на Виннипегский университет не работает.
WinEunuuchs2Unix

1
@TimKennedy Не за что .... из Эдмонтона :)
WinEunuuchs2Unix

'case "$ -" in' не работает в скриптах bash.
Хобади

@ Hobadee - у каждого bash, к которому у меня есть доступ, есть $, как dashи у ksh. даже в оболочках Solaris с ограниченным доступом. Какую платформу вы пытаетесь использовать там, где она не работает? Что case "$-" in *i*) echo true ;; *) echo false ;; esacпоказывает тебе?
Тим Кеннеди

7

Сначала получите PID cron, затем PID родительского текущего процесса (PPID) и сравните их:

CRONPID=$(ps ho %p -C cron)
PPID=$(ps ho %P -p $$)
if [ $CRONPID -eq $PPID ] ; then echo Cron is our parent. ; fi

Если ваш скрипт запускается другим процессом, который мог быть запущен cron, вы можете вернуться к родительским PID, пока не доберетесь до $ CRONPID или 1 (PID init).

может быть что-то вроде этого (Untested-But-It-Might-Work <TM>):

PPID=$$   # start from current PID
CRON_IS_PARENT=0
CRONPID=$(ps ho %p -C cron)
while [ $CRON_IS_PARENT -ne 1 ] && [ $PPID -ne 1 ] ; do
  PPID=$(ps ho %P -p $PPID)
  [ $CRONPID -eq $PPID ] && CRON_IS_PARENT=1
done

От Deian: это версия, протестированная на RedHat Linux

# start from current PID
MYPID=$$
CRON_IS_PARENT=0
# this might return a list of multiple PIDs
CRONPIDS=$(ps ho %p -C crond)

CPID=$MYPID
while [ $CRON_IS_PARENT -ne 1 ] && [ $CPID -ne 1 ] ; do
        CPID_STR=$(ps ho %P -p $CPID)
        # the ParentPID came up as a string with leading spaces
        # this will convert it to int
        CPID=$(($CPID_STR))
        # now loop the CRON PIDs and compare them with the CPID
        for CRONPID in $CRONPIDS ; do
                [ $CRONPID -eq $CPID ] && CRON_IS_PARENT=1
                # we could leave earlier but it's okay like that too
        done
done

# now do whatever you want with the information
if [ "$CRON_IS_PARENT" == "1" ]; then
        CRON_CALL="Y"
else
        CRON_CALL="N"
fi

echo "CRON Call: ${CRON_CALL}"

1
В Solaris cron запускает оболочку, а оболочка запускает скрипт, который сам запускает другую оболочку. Таким образом, родительский pid в скрипте не является pid cron.
ceving

4

Если ваш файл скрипта вызывается cronи содержит в первой строке оболочку, как #!/bin/bashвам нужно найти имя родительского-родителя для вашей цели.

1) cronвызывается в данный момент в вашем crontab, выполняя оболочку 2) оболочка выполняет ваш скрипт 3) ваш скрипт выполняется

Родительский PID доступен в bash как переменная $PPID. Команда psдля получения родительского PID родительского PID:

PPPID=`ps h -o ppid= $PPID`

но нам нужно имя команды, а не pid, поэтому мы называем

P_COMMAND=`ps h -o %c $PPPID`

теперь нам просто нужно проверить результат для "cron"

if [ "$P_COMMAND" == "cron" ]; then
  RUNNING_FROM_CRON=1
fi

Теперь вы можете проверить в любом месте вашего скрипта

if [ "$RUNNING_FROM_CRON" == "1" ]; then
  ## do something when running from cron
else
  ## do something when running from shell
fi

Удачи!


Это работает только для Linux PS. Для MacOS (а также для Linux, может быть, * BSD тоже), вы можете использовать следующую P_COMMAND:P_COMMAND=$(basename -a $(ps h -o comm $PPPID))
mdd

1

Работает на FreeBSD или на Linux:

if [ "Z$(ps o comm="" -p $(ps o ppid="" -p $$))" == "Zcron" -o \
     "Z$(ps o comm="" -p $(ps o ppid="" -p $(ps o ppid="" -p $$)))" == "Zcron" ]
then
    echo "Called from cron"
else
    echo "Not called from cron"
fi

Вы можете пойти так далеко вверх по дереву процессов, как пожелаете.


1

Типичное решение вопроса «является ли мой вывод терминалом или я запускаю из скрипта»:

( : > /dev/tty) && dev_tty_good=y || dev_tty_good=n

0

Простое echo $TERM | mail me@domain.comв cron показало мне, что как в Linux, так и в AIX cron, похоже, установлен $TERMв «тупой».

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


0

Нет никакого авторитетного ответа, но переменные prompt ( $PS1) и Terminal ( $TERM) здесь вполне приличные. Некоторые системы установлены, в TERM=dumbто время как большинство оставляют это пустым, поэтому мы просто проверим:

if [ "${TERM:-dumb}$PS1" != "dumb" ]; then
  echo "This is not a cron job"
fi

Приведенный выше код заменяет слово «тупой», если для него нет значения $TERM. Таким образом, условный вызов срабатывает, когда нет $TERMили $TERMзадано значение «немой» или если $PS1переменная не пуста.

Я протестировал это на Debian 9 ( TERM=), CentOS 6.4 и 7.4 ( TERM=dumb) и FreeBSD 7.3 ( TERM=).

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.