¿Cómo simular el entorno con el que cron ejecuta un script?

253

Normalmente tengo varios problemas con la forma en que cron ejecuta los scripts, ya que normalmente no tienen la configuración de mi entorno. ¿Hay alguna manera de invocar bash (?) De la misma manera que lo hace cron para poder probar los scripts antes de instalarlos?

Jorge Vargas
fuente
Sugeriría esta solución: unix.stackexchange.com/questions/27289/…
rudi
Llevando a @gregseth un poco más lejos, di esta solución: unix.stackexchange.com/questions/27289/…
Robert Brisita

Respuestas:

385

Agregue esto a su crontab (temporalmente):

* * * * * env > ~/cronenv

Después de que se ejecute, haga esto:

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

Esto supone que su cron ejecuta / bin / sh, que es el valor predeterminado independientemente del shell predeterminado del usuario.

mmccoo
fuente
55
nota: si agrega eso al global / etc / crontab, también necesitará el nombre de usuario. Por ejemplo, * * * * * root env> ~ / cronenv
Greg
10
Buena idea simple. Para los impacientes, use '* * * * *' para correr el próximo minuto y recuerde apagar nuevamente cuando haya terminado de jugar ;-)
Mads Buus
55
@Madsn Para volver al anterior bash shell intente: salir
spkane
55
La importancia de esta respuesta no puede ser subestimada. Digno de una inclusión de párrafo en un libro.
Xofo
1
¿ env - cat ~ / cronenv` / bin / sh` también debería escribirse como trabajo cron? por favor da un ejemplo
JavaSa
61

Cron proporciona solo este entorno de forma predeterminada:

  • HOME directorio de inicio del usuario
  • LOGNAME inicio de sesión del usuario
  • PATH=/usr/bin:/usr/sbin
  • SHELL=/usr/bin/sh

Si necesita más, puede obtener un script donde defina su entorno antes de la tabla de programación en el crontab.

Gregseth
fuente
77
.por lo general ya no forma parte PATH, por razones de seguridad .
l0b0
49

Par de enfoques:

  1. Exportar cron env y obtenerlo:

    Añadir

    * * * * * env > ~/cronenv

    a su crontab, déjelo correr una vez, apáguelo, luego ejecute

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

    Y ahora estás dentro de una shsesión que tiene el entorno de cron

  2. Lleva tu entorno a cron

    Puede omitir el ejercicio anterior y simplemente hacer una . ~/.profiletarea en frente de su trabajo cron, p. Ej.

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

    Las dos soluciones anteriores todavía fallan porque proporcionan un entorno conectado a una sesión X en ejecución, con acceso a dbusetc. Por ejemplo, en Ubuntu, nmcli(Network Manager) funcionará en los dos enfoques anteriores, pero aún falla en cron.

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

    Agregue la línea anterior al cron, déjelo correr una vez, apáguelo nuevamente. Conéctese a su sesión de pantalla (screen -r). Si está comprobando que la sesión de pantalla se ha creado (con ps), tenga en cuenta que a veces están en mayúsculas (por ejemplo ps | grep SCREEN)

    Ahora incluso nmcliy similares fallarán.

Galleta
fuente
22

Tu puedes correr:

env - your_command arguments

Esto ejecutará your_command con un entorno vacío.

dimba
fuente
44
cron no se ejecuta en un entorno completamente vacío, ¿verdad?
jldupont
2
gregseth identificó las variables incluidas en el entorno por cron. Puede incluir esas variables en la línea de comando. $ env - PATH = "$ PATH" comando args
DragonFax
44
@DragonFax @dimba que uso, env - HOME="$HOME" LOGNAME="$USER" PATH="/usr/bin:/bin" SHELL="$(which sh)" command argumentsque parece hacer el truco
l0b0
Este es un gran método simple para probar scripts en entornos "hostiles" o desconocidos. Si eres lo suficientemente explícito como para que se ejecute en esto, se ejecutará bajo cron.
Oli
13

Respondiendo seis años después: el problema del desajuste del entorno es uno de los problemas resueltos por los systemd"temporizadores" como reemplazo de cron. Ya sea que ejecute el "servicio" systemd desde la CLI o vía cron, recibe exactamente el mismo entorno, evitando el problema de falta de coincidencia del entorno.

El problema más común para hacer que los trabajos cron fallen cuando pasan manualmente es el $PATHajuste predeterminado restrictivo establecido por cron, que es esto en Ubuntu 16.04:

"/usr/bin:/bin"

Por el contrario, el valor predeterminado $PATHestablecido systemden Ubuntu 16.04 es:

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

Por lo tanto, ya hay una mejor posibilidad de que un temporizador systemd encuentre un binario sin más problemas.

La desventaja de los temporizadores systemd es que hay un poco más de tiempo para configurarlos. Primero crea un archivo de "servicio" para definir lo que desea ejecutar y un archivo "temporizador" para definir la programación para ejecutarlo y finalmente "habilita" el temporizador para activarlo.

Mark Stosberg
fuente
10

Cree un trabajo cron que ejecute env y redirija stdout a un archivo. Use el archivo junto a "env -" para crear el mismo entorno que un trabajo cron.

Jens Carlberg
fuente
Lo siento, esto me confundió. ¿no es suficiente "env - script"?
Jorge Vargas
Eso te dará un ambiente vacío. Cuando ejecuta scripts a través de cron, el entorno no está vacío.
Jens Carlberg el
3

No olvide que, dado que el padre de cron es init, ejecuta programas sin un terminal de control. Puede simular eso con una herramienta como esta:

http://libslack.org/daemon/

Randy Proctor
fuente
2

Por defecto, cronejecuta sus trabajos utilizando la idea de su sistema sh. Este podría ser el actual Bourne shell o dash, ash, ksho bash(u otro) se enlazan a sh(y como resultado se ejecuta en modo POSIX).

Lo mejor que puede hacer es asegurarse de que sus scripts tengan lo que necesitan y asumir que no se les proporciona nada. Por lo tanto, debe usar especificaciones de directorio completas y establecer variables de entorno como $PATHusted.

Pausado hasta nuevo aviso.
fuente
Esto es exactamente lo que estoy tratando de resolver. Tenemos muchos problemas con los scripts que suponen algo por error. Haciendo rutas completas y estableciendo variables env y toda la basura termina con horribles líneas cron inimaginables
Jorge Vargas
re * sh lo siento, crecí con bash = shell, así que me resulta difícil recordar las alternativas (y a veces mejores) conchas.
Jorge Vargas
1
@Jorge: las líneas en el crontab deberían ser bastante cortas. Debe hacer toda la configuración que necesita dentro del script (o un script de contenedor). Aquí hay una línea típica de un crontab como ejemplo: 0 0 * * 1 /path/to/executable >/dev/null 2>&1y luego, dentro de "ejecutable", establecería valores para $PATH, etc., y usaría especificaciones de directorio completas para ingresar y sacar archivos, etc. Por ejemplo:/path/to/do_something /another/path/input_file /another/path/to/output_file
Pausado hasta nuevo aviso.
1

Otra forma simple que he encontrado (pero puede ser propenso a errores, todavía estoy probando) es obtener los archivos de perfil de su usuario antes de su comando.

Edición de un script /etc/cron.d/:

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

Se convertiría en:

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

Sucio, pero hizo el trabajo por mí. ¿Hay alguna manera de simular un inicio de sesión? ¿Solo un comando que podrías ejecutar?bash --loginno funcionó Sin embargo, parece que esa sería la mejor manera de hacerlo.

EDITAR: Esta parece ser una solución 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
fuente
1

La respuesta aceptada ofrece una forma de ejecutar un script con el entorno que usaría cron. Como otros señalaron, este no es el único criterio necesario para depurar trabajos cron.

De hecho, cron también usa un terminal no interactivo, sin una entrada adjunta, etc.

Si eso ayuda, he escrito un script que permite ejecutar sin problemas un comando / script como lo ejecutaría cron. Invoque con su comando / script como primer argumento y estará bien.

Este script también está alojado (y posiblemente actualizado) en 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
fuente
0

La respuesta https://stackoverflow.com/a/2546509/5593430 muestra cómo obtener el entorno cron y usarlo para su script. Pero tenga en cuenta que el entorno puede variar según el archivo crontab que utilice. Creé tres entradas cron diferentes para salvar el entorno a través de env > log. Estos son los resultados en un Amazon Linux 4.4.35-33.55.amzn1.x86_64.

1. Global / etc / crontab con usuario 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 de usuario 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 en /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

Lo más importante PATH, PWDy HOMEdifieren. Asegúrese de configurarlos en sus scripts cron para confiar en un entorno estable.

Maikel
fuente
-8

No creo que exista; la única forma en que sé probar un trabajo cron es configurarlo para que se ejecute uno o dos minutos en el futuro y luego esperar.

jn80842
fuente
Es por eso que estoy pidiendo simulación
Jorge Vargas