Come simulare l'ambiente con cron esegue uno script?


253

Normalmente ho diversi problemi con il modo in cui cron esegue gli script in quanto normalmente non hanno il mio ambiente impostato. C'è un modo per invocare bash (?) Nello stesso modo in cui cron fa in modo da poter testare gli script prima di installarli?


Vorrei suggerire questa soluzione: unix.stackexchange.com/questions/27289/…
rudi

Prendendo un po 'più in là @gregseth, ho dato questa soluzione: unix.stackexchange.com/questions/27289/…
Robert Brisita,

Risposte:


385

Aggiungi questo al tuo crontab (temporaneamente):

* * * * * env > ~/cronenv

Dopo l'esecuzione, procedere come segue:

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

Ciò presuppone che cron esegua / bin / sh, che è l'impostazione predefinita indipendentemente dalla shell predefinita dell'utente.


5
nota: se lo aggiungi al globale / etc / crontab, avrai bisogno anche del nome utente. Ad esempio * * * * * root env> ~ / cronenv
Greg

10
Buona, semplice idea. Per l'impaziente usa '* * * * *' per correre al minuto successivo e ricordati di spegnere di nuovo quando hai finito di giocare ;-)
Mads Buus

5
@Madsn Per tornare alla precedente shell bash prova: esci
spkane il

5
L'importanza di questa risposta non può essere sottovalutata. Degno dell'inclusione di un paragrafo in un libro.
Xofo,

1
è env - cat ~ / cronenv` / bin / sh` dovrebbe essere scritto come processo di cron anche? per favore, fai un esempio
JavaSa

61

Cron fornisce solo questo ambiente per impostazione predefinita:

  • HOME home directory dell'utente
  • LOGNAME accesso dell'utente
  • PATH=/usr/bin:/usr/sbin
  • SHELL=/usr/bin/sh

Se hai bisogno di più, puoi creare uno script in cui definisci il tuo ambiente prima della tabella di pianificazione nel crontab.


7
.di solito non fa PATHpiù parte, per motivi di sicurezza .
l0b0

49

Coppia di approcci:

  1. Esporta cron env e sorgente:

    Inserisci

    * * * * * env > ~/cronenv

    sul tuo crontab, lascialo funzionare una volta, spegnilo di nuovo, quindi corri

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

    E ora sei all'interno di una shsessione che ha l'ambiente di cron

  2. Porta il tuo ambiente a cron

    Potresti saltare sopra l'esercizio e fare semplicemente un lavoro . ~/.profiledavanti al tuo cron, ad es

    * * * * * . ~/.profile; your_command
  3. Usa lo schermo

    Sopra due soluzioni non riescono ancora in quanto forniscono un ambiente collegato a una sessione X in esecuzione, con accesso a dbusecc. Ad esempio, su Ubuntu, nmcli(Network Manager) funzionerà con due approcci precedenti, ma fallirà comunque in cron.

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

    Aggiungi la riga sopra a cron, lasciala funzionare una volta, disattivalo. Connettiti alla sessione dello schermo (schermo -r). Se stai verificando che la sessione dello schermo è stata creata (con ps), tieni presente che a volte sono in maiuscolo (ad esps | grep SCREEN )

    Ora anche nmclie simili falliranno.


22

Puoi eseguire:

env - your_command arguments

Questo eseguirà your_command con un ambiente vuoto.


4
cron non funziona in un ambiente completamente vuoto, vero?
jldupont,

2
gregseth ha identificato le variabili incluse nell'ambiente da cron. È possibile includere tali variabili nella riga di comando. $ env - PATH = "$ PATH" comando args
DragonFax

4
@DragonFax @dimba che uso, env - HOME="$HOME" LOGNAME="$USER" PATH="/usr/bin:/bin" SHELL="$(which sh)" command argumentsche sembra fare il trucco
l0b0

Questo è un metodo semplice per testare gli script in ambienti "ostili" o sconosciuti. Se sei abbastanza esplicito che verrà eseguito in questo, verrà eseguito in cron.
Oli,


13

Risposta a distanza di sei anni: il problema di mancata corrispondenza ambientale è uno dei problemi risolti dai systemd"timer" in sostituzione di cron. Sia che si esegua il "servizio" systemd dalla CLI o tramite cron, esso riceve esattamente lo stesso ambiente, evitando il problema di mancata corrispondenza dell'ambiente.

Il problema più comune che causa il fallimento dei lavori cron quando passano manualmente è l'impostazione predefinita restrittiva $PATHimpostata da cron, che è questa su Ubuntu 16.04:

"/usr/bin:/bin"

Al contrario, l'impostazione predefinita $PATHimpostata systemdsu Ubuntu 16.04 è:

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

Quindi c'è già una migliore possibilità che un timer di sistema trovi un binario senza ulteriori problemi.

Il rovescio della medaglia con i timer di systemd è che c'è un po 'più di tempo per configurarli. Innanzitutto si crea un file "di servizio" per definire ciò che si desidera eseguire e un file "timer" per definire la pianificazione su cui eseguirlo e infine "abilitare" il timer per attivarlo.


10

Crea un cron job che esegue env e reindirizza stdout su un file. Utilizzare il file accanto a "env -" per creare lo stesso ambiente di un processo cron.


Scusa, questo mi ha confuso. "env - script" non è abbastanza?
Jorge Vargas,

Questo ti darà un ambiente vuoto. Quando si eseguono script tramite cron, l'ambiente non è vuoto.
Jens Carlberg,


2

Di default, cronesegue i suoi lavori usando qualunque sia l'idea del tuo sistema sh. Questa potrebbe essere la shell Bourne effettivo o dash, ash, ksho bash(o altro) collegato simbolicamente a sh(e di conseguenza l'esecuzione in modalità POSIX).

La cosa migliore da fare è assicurarsi che i tuoi script abbiano ciò di cui hanno bisogno e supporre che non sia previsto nulla per loro. Pertanto, è necessario utilizzare le specifiche di directory complete e impostare variabili di ambiente come $PATHte.


Questo è esattamente ciò che sto cercando di risolvere. Abbiamo molti problemi con gli script che assumono qualcosa per errore. Fare percorsi completi e impostare variabili env e tutta la spazzatura finisce con orribili enormi linee cronologiche non mantenibili
Jorge Vargas

mi spiace di essere cresciuto con bash = shell, quindi per me è difficile ricordare le shell alternative (e talvolta migliori).
Jorge Vargas,

1
@Jorge: le linee nel crontab dovrebbero essere piuttosto brevi. È necessario eseguire tutte le impostazioni necessarie all'interno dello script (o uno script wrapper). Ecco una linea tipica di un crontab come esempio: 0 0 * * 1 /path/to/executable >/dev/null 2>&1e poi, all'interno di "eseguibile", imposterei i valori per $PATH, ecc., E utilizzerei le specifiche complete della directory per inserire e produrre file, ecc. Ad esempio:/path/to/do_something /another/path/input_file /another/path/to/output_file
Messo in pausa fino a ulteriore avviso.

1

Un altro modo semplice che ho trovato (ma potrebbe essere soggetto a errori, sto ancora testando) è quello di trovare i file del profilo dell'utente prima del tuo comando.

Modifica di uno script /etc/cron.d/:

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

Si trasformerebbe in:

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

Sporco, ma ha fatto il lavoro per me. C'è un modo per simulare un login? Solo un comando che potresti eseguire?bash --loginnon ha funzionato. Sembra che sarebbe il modo migliore per andare però.

EDIT: questa sembra essere una soluzione solida: 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

La risposta accettata fornisce un modo per eseguire uno script con l'ambiente usato da cron. Come altri hanno sottolineato, questo non è l'unico criterio necessario per il debug di cron job.

In effetti, cron utilizza anche un terminale non interattivo, senza un input collegato, ecc.

Se questo aiuta, ho scritto uno script che consente di eseguire un comando / script indolore come sarebbe eseguito da cron. Invocalo con il tuo comando / script come primo argomento e sei bravo.

Questo script è anche ospitato (e possibilmente aggiornato) su 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

La risposta https://stackoverflow.com/a/2546509/5593430 mostra come ottenere l'ambiente cron e utilizzarlo per lo script. Ma tieni presente che l'ambiente può differire in base al file crontab che usi. Ho creato tre voci cron diverse per salvare l'ambiente tramite env > log. Questi sono i risultati su un Amazon Linux 4.4.35-33.55.amzn1.x86_64.

1. Global / etc / crontab con l'utente 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 utente di 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 in /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

Ancora più importante PATH, PWDe HOMEdifferiscono. Assicurati di impostarli negli script cron per fare affidamento su un ambiente stabile.


-8

Non credo che ci sia; l'unico modo che conosco per testare un cron job è di configurarlo per eseguire un minuto o due in futuro e quindi attendere.


Questo è il motivo per cui chiedo di simulare
Jorge Vargas il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.