Перевірте, чи скрипт запускається cron, а не викликається вручну


23

Чи є якась змінна, яку встановлює cron, коли вона запускає програму? Якщо сценарій працює за допомогою cron, я хотів би пропустити деякі частини; в іншому випадку викликайте ці частини.

Як я можу знати, якщо сценарій Bash запускається cron?


Чому ти не просто нас ps?
тердон


@terdon: можливо, тому, що psце досить погано задокументовано (особливо версія Linux, яка підтримує кілька різних стилів синтаксису), а сторінка man є ще більш щільною та виразною, ніж більшість інструментів. Я підозрюю, що більшість людей навіть не розуміють, наскільки корисним та універсальним 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

відповідь cas працює дуже добре і може бути використана і для чого-небудь іншого
Deian

19

Сценарії, запущені з крона, не запускаються в інтерактивних оболонках. Ні сценарії запуску. Відмінність полягає в тому, що інтерактивні оболонки мають 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 не працює, коли перевіряється, чи запускається скрипт системою чи ні. $ - one
do

1
Посилання на ваш університет у Вінніпезі перервано.
WinEunuuchs2Unix

1
@TimKennedy Вітаємо вас .... з Едмонтона :)
WinEunuuchs2Unix

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

@Hobadee - кожен, у кого bashя маю доступ, має $ -, як dashі ksh. навіть обмежені снаряди в Соляріс є. Яку платформу ви намагаєтеся використовувати її там, де вона не працює? Що 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).

щось подібне, можливо, (Неперевірено-але-це-могло б працювати <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.
припинення

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.