Tenho a seguinte configuração de tarefa cron no Debian 12:
/etc/cron.d/jonathan-test
:
SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
* * * * * jonathan /home/jonathan/test1.sh >> /home/jonathan/test.log 2>&1
/home/jonathan/test1.sh
:
#!/usr/bin/env bash
export PATH="/home/jonathan/mytestdir:${PATH}"
echo "test1.sh -> PATH=${PATH}"
export PAAATH="this_is_a_test"
echo "test1.sh -> PAAATH=${PAAATH}"
exec "${HOME}/test2.sh"
/home/jonathan/test2.sh
:
#!/usr/bin/env bash
echo "test2.sh -> PATH=${PATH}"
echo "test2.sh -> PAAATH=${PAAATH}"
Quando executado, ele grava o seguinte em /home/jonathan/test.log
:
test1.sh -> PATH=/home/jonathan/mytestdir:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
test1.sh -> PAAATH=this_is_a_test
test2.sh -> PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
test2.sh -> PAAATH=this_is_a_test
Como você pode ver, a $PATH
variável não é preservada por exec
.
Este é um exemplo simplificado e artificial do meu problema real, que é executar o pyenv a partir de um cron job. Se eu mudar meu cron.d
arquivo para isto:
SHELL=/bin/bash
PYENV_ROOT=/opt/pyenv
PATH=/opt/pyenv/shims:/opt/pyenv/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
* * * * * jonathan python --version >> /home/jonathan/test.log 2>&1
Então eu escrevo isso no arquivo de saída:
/opt/pyenv/libexec/pyenv-exec: line 24: pyenv-version-name: command not found
Ele executa corretamente /opt/pyenv/shims/python
. Isso é apenas um script bash que executa pyenv exec python --version
. Ele executa corretamente /opt/pyenv/bin/pyenv
, que é um link simbólico para /opt/pyenv/libexec/pyenv
, que é um script bash que modifica $PATH
para incluir /opt/pyenv/libexec
(e sim, ele exporta!) e executa /opt/pyenv/libexec/pyenv-exec
, que é outro script bash que tenta fazer PYENV_VERSION="$(pyenv-version-name)"
na linha 24, o que resulta no erro acima, porque /opt/pyenv/libexec
não está em $PATH
. Eu reduzi para o exemplo simplificado acima. A mesma pyenv
configuração exata com apenas variáveis de ambiente e sem a integração do shell funciona muito bem quando não é executada a partir do cron.
Para sua informação, não há sudo
lugar nenhum nisso, e eu posso reproduzi-lo como outros usuários também. Então não parece estar relacionado a secure_path
em /etc/sudoers
.
Vou responder minha própria pergunta, já que eu descobri.
O problema era que eu tinha um
$BASH_ENV
conjunto personalizado. Quando invocado de forma não interativa , o bash não lê.profile
,.bash_profile
, ou.bashrc
( docs ). Este é um problema comum com tarefas cron porque elas não terão o ambiente que muitas pessoas esperam. No entanto, você pode usar$BASH_ENV
. Quando o bash é invocado de forma não interativa, ele se comporta como se fizesse isso:Então, em uma tentativa de ter um ambiente consistente, independentemente de meus scripts serem executados no cron ou não, apontei
$BASH_ENV
para um arquivo que se parecia com este:Então, meus trabalhos cron teriam o mesmo ambiente como se fossem invocados de um shell de login. Havia dois problemas com isso. Primeiro,
/etc/profile
estava definindo$PATH
em vez de adicionar a ele. Segundo, eu não percebi que$BASH_ENV
é carregado para cada sub-shell, então meu$PATH
estava constantemente sendo redefinido para cada sub-shell.Então, eu mudei meu
/etc/profile
para verificar caminhos específicos$PATH
e adicioná-los se eles não estiverem lá. E eu mudei meu$BASH_ENV
arquivo para isto:(Eu provavelmente não deveria usar um nome de variável que começa com
BASH_
, mas não consegui pensar em como chamá-lo.)