Как имитировать среду, с которой cron выполняет скрипт?


253

У меня обычно есть несколько проблем с тем, как cron выполняет сценарии, так как они обычно не имеют моей настройки среды. Есть ли способ вызвать bash (?) Таким же образом, как это делает cron, чтобы я мог тестировать сценарии перед их установкой?


Я хотел бы предложить это решение: unix.stackexchange.com/questions/27289/…
rudi

Продолжая @gregseth, я дал следующее решение: unix.stackexchange.com/questions/27289/…
Роберт Брисита,

Ответы:


385

Добавьте это в ваш crontab (временно):

* * * * * env > ~/cronenv

После запуска сделайте это:

env - `cat ~/cronenv` /bin/sh

Это предполагает, что ваш cron запускает / bin / sh, который используется по умолчанию независимо от оболочки пользователя по умолчанию.


5
примечание: если вы добавите это в глобальный / etc / crontab, вам также понадобится имя пользователя. Например * * * * * root env> ~ / cronenv
Грег

10
Хорошая, простая идея. Для нетерпеливых используйте «* * * * *» для запуска в следующую минуту, и не забудьте выключить снова, когда вы закончите играть ;-)
Mads Buus

5
@Madsn Чтобы вернуться к предыдущей оболочке bash, попробуйте: exit
spkane

5
Важность этого ответа нельзя недооценивать. Достойны включения абзаца в книгу.
Xofo

1
является env - кошка ~ / cronenv` / бен / sh` должно быть написано , как хроны также? приведите пожалуйста пример
JavaSa

61

Cron предоставляет только эту среду по умолчанию:

  • HOME домашний каталог пользователя
  • LOGNAME логин пользователя
  • PATH=/usr/bin:/usr/sbin
  • SHELL=/usr/bin/sh

Если вам нужно больше, вы можете создать сценарий, в котором вы определяете свою среду перед таблицей планирования в crontab.


7
.как правило, больше не является частью PATHпо соображениям безопасности .
10

49

Пара подходов:

  1. Экспортируйте cron env и поставьте его:

    Добавить

    * * * * * env > ~/cronenv

    в ваш crontab, дайте ему один раз запустить, выключите его, затем запустите

    env - `cat ~/cronenv` /bin/sh

    И теперь вы находитесь в shсеансе, в котором есть среда cron

  2. Принеси свое окружение в cron

    Вы можете пропустить вышеупомянутое упражнение и просто сделать . ~/.profileперед своей работой, например:

    * * * * * . ~/.profile; your_command
  3. Использовать экран

    Вышеперечисленные два решения по-прежнему терпят неудачу в том, что они предоставляют среду, подключенную к запущенному сеансу X, с доступом к ним dbusи т. Д. Например, в Ubuntu nmcli(Network Manager) будет работать в двух вышеупомянутых подходах, но все равно не будет работать в cron.

    * * * * * /usr/bin/screen -dm

    Добавьте указанную выше строку в cron, дайте ей один раз поработать, снова выключите. Подключитесь к сеансу экрана (screen -r). Если вы проверяете, что сеанс экрана был создан (с ps), имейте в виду, что они иногда в столицах (например ps | grep SCREEN)

    Теперь даже nmcliи подобное не получится.


22

Вы можете запустить:

env - your_command arguments

Это запустит your_command с пустой средой.


4
Cron не работает в абсолютно пустой среде, не так ли?
2010 г.

2
Грегсет идентифицировал переменные, включенные в окружение cron. Вы можете включить эти переменные в командной строке.
Аргументы

4
@DragonFax @dimba Я использую, env - HOME="$HOME" LOGNAME="$USER" PATH="/usr/bin:/bin" SHELL="$(which sh)" command argumentsкоторый, кажется, делает
свое дело

Это отличный простой метод для тестирования скриптов в «враждебной» или неизвестной среде. Если вы достаточно откровенны, что он будет работать в этом, он будет работать под Cron.
Оли


13

Отвечая шесть лет спустя: проблема несоответствия среды является одной из проблем, решаемых systemd«таймерами» в качестве замены хрон. Независимо от того, запускаете ли вы «service» systemd из CLI или через cron, он получает точно такую ​​же среду, избегая проблемы несоответствия среды.

Наиболее распространенная проблема, приводящая к сбою заданий cron при их ручном прохождении, - это ограничение по умолчанию, $PATHустановленное cron, например, в Ubuntu 16.04:

"/usr/bin:/bin"

В отличие от этого , по умолчанию $PATHустановленный systemdна Ubuntu 16.04 является:

"/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"

Так что уже есть больше шансов, что системный таймер найдет двоичный файл без лишних хлопот.

Недостаток системных таймеров - немного больше времени для их настройки. Сначала вы создаете файл «service», чтобы определить, что вы хотите запустить, и файл «timer», чтобы определить расписание его запуска и, наконец, «включить» таймер, чтобы активировать его.


10

Создайте задание cron, которое запускает env и перенаправляет stdout в файл. Используйте файл вместе с «env -», чтобы создать ту же среду, что и задание cron.


Извините, это смутило меня. не достаточно ли env-script?
Хорхе Варгас

Это даст вам пустую среду. Когда вы запускаете скрипты через cron, среда не пуста.
Йенс Карлберг

3

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

http://libslack.org/daemon/


2

По умолчанию cronвыполняет свои задания, используя то, о чем ваша система представляет sh. Это может быть фактическим Борна оболочка или dash, ash, kshили bash(или другой) слинковано в sh(и , как следствие , работающем в режиме POSIX).

Лучше всего убедиться, что в ваших скриптах есть то, что им нужно, и предположить, что для них ничего не предусмотрено. Поэтому вы должны использовать полные спецификации каталогов и устанавливать такие переменные среды, как $PATHвы.


Это именно то, что я пытаюсь решить. Мы получаем массу проблем со скриптами, которые предполагают что-то по ошибке. Выполнение полных путей и установка переменных env и всего мусора заканчивается ужасными огромными не поддерживаемыми линиями cron
Хорхе Варгас

Мне жаль, что я вырос на bash = shell, поэтому мне сложно вспомнить альтернативные (а иногда и лучшие) оболочки.
Хорхе Варгас

1
@Jorge: строки в crontab должны быть довольно короткими. Вы должны выполнить все необходимые настройки внутри скрипта (или скрипта-обёртки). Вот типичная строка из crontab в качестве примера: 0 0 * * 1 /path/to/executable >/dev/null 2>&1затем в «исполняемом файле» я бы установил значения $PATHи т. Д., А также использовал полные спецификации каталогов для ввода и вывода файлов и т. Д. Например:/path/to/do_something /another/path/input_file /another/path/to/output_file
Пауза до дальнейшего уведомления.

1

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

Редактирование скрипта /etc/cron.d/:

* * * * * user1 comand-that-needs-env-vars

Превратился бы в:

* * * * * user1 source ~/.bash_profile; source ~/.bashrc; comand-that-needs-env-vars

Грязный, но он сделал работу за меня. Есть ли способ симулировать логин? Просто команда, которую вы могли бы запустить? bash --loginне работал Похоже, это был бы лучший путь.

РЕДАКТИРОВАТЬ: Это, кажется, твердое решение: http://www.epicserve.com/blog/2012/feb/7/my-notes-cron-directory-etccrond-ubuntu-1110/

* * * * * root su --session-command="comand-that-needs-env-vars" user1 -l

1

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

Действительно, cron также использует неинтерактивный терминал, без подключенного входа и т. Д.

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

Этот скрипт также размещен (и, возможно, обновлен) на Github .

#!/bin/bash
# Run as if it was called from cron, that is to say:
#  * with a modified environment
#  * with a specific shell, which may or may not be bash
#  * without an attached input terminal
#  * in a non-interactive shell

function usage(){
    echo "$0 - Run a script or a command as it would be in a cron job, then display its output"
    echo "Usage:"
    echo "   $0 [command | script]"
}

if [ "$1" == "-h" -o "$1" == "--help" ]; then
    usage
    exit 0
fi

if [ $(whoami) != "root" ]; then
    echo "Only root is supported at the moment"
    exit 1
fi

# This file should contain the cron environment.
cron_env="/root/cron-env"
if [ ! -f "$cron_env" ]; then
    echo "Unable to find $cron_env"
    echo "To generate it, run \"/usr/bin/env > /root/cron-env\" as a cron job"
    exit 0
fi

# It will be a nightmare to expand "$@" inside a shell -c argument.
# Let's rather generate a string where we manually expand-and-quote the arguments
env_string="/usr/bin/env -i "
for envi in $(cat "$cron_env"); do
   env_string="${env_string} $envi "
done

cmd_string=""
for arg in "$@"; do
    cmd_string="${cmd_string} \"${arg}\" "
done

# Which shell should we use?
the_shell=$(grep -E "^SHELL=" /root/cron-env | sed 's/SHELL=//')
echo "Running with $the_shell the following command: $cmd_string"


# Let's route the output in a file
# and do not provide any input (so that the command is executed without an attached terminal)
so=$(mktemp "/tmp/fakecron.out.XXXX")
se=$(mktemp "/tmp/fakecron.err.XXXX")
"$the_shell" -c "$env_string $cmd_string" >"$so" 2>"$se" < /dev/null

echo -e "Done. Here is \033[1mstdout\033[0m:"
cat "$so"
echo -e "Done. Here is \033[1mstderr\033[0m:"
cat "$se"
rm "$so" "$se"

0

Ответ https://stackoverflow.com/a/2546509/5593430 показывает, как получить среду cron и использовать ее для своего сценария. Но имейте в виду, что среда может отличаться в зависимости от используемого файла crontab. Я создал три разные записи cron, чтобы сохранить среду через env > log. Это результаты на Amazon Linux 4.4.35-33.55.amzn1.x86_64.

1. Глобальный / etc / crontab с пользователем root

MAILTO=root
SHELL=/bin/bash
USER=root
PATH=/sbin:/bin:/usr/sbin:/usr/bin
PWD=/
LANG=en_US.UTF-8
SHLVL=1
HOME=/
LOGNAME=root
_=/bin/env

2. Пользовательский crontab root ( crontab -e)

SHELL=/bin/sh
USER=root
PATH=/usr/bin:/bin
PWD=/root
LANG=en_US.UTF-8
SHLVL=1
HOME=/root
LOGNAME=root
_=/usr/bin/env

3. Сценарий в /etc/cron.hourly/

MAILTO=root
SHELL=/bin/bash
USER=root
PATH=/sbin:/bin:/usr/sbin:/usr/bin
_=/bin/env
PWD=/
LANG=en_US.UTF-8
SHLVL=3
HOME=/
LOGNAME=root

Самое главное PATH, PWDи HOMEотличаются. Обязательно установите их в своих скриптах cron, чтобы они полагались на стабильную среду.


-8

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


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