Como simular o ambiente com o cron executa um script?

253

Normalmente, tenho vários problemas com a forma como o cron executa scripts, pois eles normalmente não têm a configuração do meu ambiente. Existe uma maneira de chamar bash (?) Da mesma forma que o cron faz para que eu possa testar scripts antes de instalá-los?

Jorge Vargas
fonte
Eu sugeriria esta solução: unix.stackexchange.com/questions/27289/…
rudi
Tomando @gregseth um pouco mais, eu dei esta solução: unix.stackexchange.com/questions/27289/...
Robert Brisita

Respostas:

385

Adicione isso ao seu crontab (temporariamente):

* * * * * env > ~/cronenv

Após a execução, faça o seguinte:

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

Isso pressupõe que seu cron execute / bin / sh, que é o padrão, independentemente do shell padrão do usuário.

mmccoo
fonte
5
nota: se adicionar isso ao global / etc / crontab, você precisará do nome de usuário também. Por exemplo, * * * * * root env> ~ / cronenv
Greg
10
Boa ideia simples. Para o uso impaciente '* * * * *' para executar minuto seguinte, e lembre-se de desligar novamente quando você terminar de jogar ;-)
Mads Buus
5
@Madsn Para retornar à tentativa shell bash anterior: saída
spkane
5
A importância desta resposta não pode ser subestimada. Digno de uma inclusão de parágrafo em um livro.
X28 de
1
é env - cat ~ / cronenv` / bin / sh` deve ser escrita como trabalho cron também? por favor, dê um exemplo
JavaSa
61

O Cron fornece apenas este ambiente por padrão:

  • HOME diretório inicial do usuário
  • LOGNAME login do usuário
  • PATH=/usr/bin:/usr/sbin
  • SHELL=/usr/bin/sh

Se você precisar de mais, poderá gerar um script no qual define seu ambiente antes da tabela de agendamento no crontab.

Gregseth
fonte
7
.geralmente não faz PATHmais parte , por razões de segurança .
L0b0
49

Duas abordagens:

  1. Exporte o cron cron e faça a origem:

    Adicionar

    * * * * * env > ~/cronenv

    ao seu crontab, deixe-o funcionar uma vez, desligue-o novamente e depois execute-o

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

    E agora você está dentro de uma shsessão que possui o ambiente do cron

  2. Traga seu ambiente para o cron

    Você pode pular o exercício acima e fazer um . ~/.profilena frente do seu trabalho cron, por exemplo

    * * * * * . ~/.profile; your_command
  3. Usar tela

    As duas soluções acima ainda falham, pois fornecem um ambiente conectado a uma sessão X em execução, com acesso a dbusetc. Por exemplo, no Ubuntu, o nmcli(Network Manager) funcionará nas duas abordagens acima, mas ainda falha no cron.

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

    Adicione a linha acima ao cron, deixe executar uma vez, desligue-o novamente. Conecte-se à sua sessão de tela (tela -r). Se você estiver verificando se a sessão da tela foi criada (com ps), esteja ciente de que às vezes estão em maiúsculas (por exemplo ps | grep SCREEN)

    Agora mesmo nmclie similar irá falhar.

Biscoito
fonte
22

Você pode correr:

env - your_command arguments

Isso executará seu_command com ambiente vazio.

dimba
fonte
4
O cron não roda em um ambiente completamente vazio, executa?
jldupont
2
Gregeg identificou as variáveis ​​incluídas no ambiente pelo cron. Você pode incluir essas variáveis ​​na linha de comando. $ env - PATH = "$ PATH" comando args
DragonFax
4
@DragonFax @dimba eu uso env - HOME="$HOME" LOGNAME="$USER" PATH="/usr/bin:/bin" SHELL="$(which sh)" command argumentso que parece fazer o truque
l0b0
Este é um ótimo método simples para testar scripts em ambientes "hostis" ou desconhecidos. Se você for explícito o suficiente para executar isso, será executado no cron.
Oli
13

Respondendo seis anos depois: o problema de incompatibilidade de ambiente é um dos problemas resolvidos pelos systemd"temporizadores" como uma substituição do cron. Se você executa o "serviço" systemd da CLI ou via cron, ele recebe exatamente o mesmo ambiente, evitando o problema de incompatibilidade de ambiente.

O problema mais comum que causa falha nos trabalhos do cron quando passam manualmente é o padrão restritivo $PATHdefinido pelo cron, que é o Ubuntu 16.04:

"/usr/bin:/bin"

Por outro lado, o padrão $PATHdefinido systemdno Ubuntu 16.04 é:

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

Portanto, já existe uma chance melhor de que um timer do systemd encontre um binário sem mais problemas.

A desvantagem dos timers do systemd é que há um pouco mais de tempo para configurá-los. Você primeiro cria um arquivo de "serviço" para definir o que deseja executar e um arquivo de "cronômetro" para definir o agendamento para execução e, finalmente, "ativar" o cronômetro para ativá-lo.

Mark Stosberg
fonte
10

Crie um trabalho cron que execute env e redirecione stdout para um arquivo. Use o arquivo ao lado de "env -" para criar o mesmo ambiente que um trabalho cron.

Jens Carlberg
fonte
Desculpe isso me confundiu. "env - script" não é suficiente?
Jorge Vargas
Isso lhe dará um ambiente vazio. Quando você executa scripts pelo cron, o ambiente não está vazio.
Jens Carlberg #
3

Não esqueça que, como o pai do cron é init, ele executa programas sem um terminal de controle. Você pode simular isso com uma ferramenta como esta:

http://libslack.org/daemon/

Randy Proctor
fonte
2

Por padrão, cronexecuta seus trabalhos usando qualquer que seja a ideia do seu sistema sh. Este poderia ser o escudo real Bourne ou dash, ash, kshou bash(ou outra) para simbolicamente sh(e, como resultado que funciona no modo de POSIX).

A melhor coisa a fazer é garantir que seus scripts tenham o que eles precisam e supor que nada lhes seja fornecido. Portanto, você deve usar especificações completas do diretório e definir variáveis ​​de ambiente como $PATHvocê.

Pausado até novo aviso.
fonte
É exatamente isso que estou tentando resolver. Temos muitos problemas com scripts que assumem algo por engano. Fazendo caminhos completos e definição de variáveis env e todo o lixo acaba com enormes linhas cron insustentável horríveis
Jorge Vargas
desculpe, eu cresci com bash = shell, então é difícil para mim lembrar as alternativas (e às vezes melhores) as conchas.
Jorge Vargas
1
@ Jorge: as linhas no crontab devem ser bastante curtas. Você deve fazer toda a configuração necessária no script (ou um script de wrapper). Aqui está uma linha típica de um crontab como exemplo: 0 0 * * 1 /path/to/executable >/dev/null 2>&1e, em "executável", eu definiria valores para $PATHetc., e usaria especificações de diretório completas para arquivos de entrada e saída, etc. Por exemplo:/path/to/do_something /another/path/input_file /another/path/to/output_file
Pausado até novo aviso.
1

Outra maneira simples que eu encontrei (mas pode estar sujeito a erros, ainda estou testando) é fonte dos arquivos de perfil do usuário antes do seu comando.

Editando um script /etc/cron.d/:

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

Transformaria em:

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

Sujo, mas fez o trabalho por mim. Existe uma maneira de simular um login? Apenas um comando que você poderia executar? bash --loginnão funcionou. Parece que seria o melhor caminho a percorrer.

EDIT: Esta parece ser uma solução sólida: 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
four43
fonte
1

A resposta aceita fornece uma maneira de executar um script com o ambiente que o cron usaria. Como outros apontaram, esse não é o único critério necessário para depurar tarefas cron.

De fato, o cron também usa um terminal não interativo, sem uma entrada anexada, etc.

Se isso ajudar, eu escrevi um script que permite a execução indolor de um comando / script, como seria executado pelo cron. Invoque-o com seu comando / script como primeiro argumento e você será bom.

Este script também está hospedado (e possivelmente atualizado) no 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"
Daladim
fonte
0

A resposta https://stackoverflow.com/a/2546509/5593430 mostra como obter o ambiente cron e usá-lo para o seu script. Mas esteja ciente de que o ambiente pode diferir dependendo do arquivo crontab que você usa. Criei três entradas cron diferentes para salvar o ambiente via env > log. Estes são os resultados em um Amazon Linux 4.4.35-33.55.amzn1.x86_64.

1. Global / etc / crontab com usuário 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. Usuário crontab de 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. Script em /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

Mais importante ainda PATH, PWDe HOMEdiferem. Certifique-se de configurá-los em seus scripts cron para confiar em um ambiente estável.

Maikel
fonte
-8

Eu não acredito que exista; a única maneira que sei testar um trabalho cron é configurá-lo para executar um ou dois minutos no futuro e aguardar.

jn80842
fonte
É por isso que eu estou pedindo simular
Jorge Vargas