Comment simuler l'environnement avec lequel cron exécute un script?


253

J'ai normalement plusieurs problèmes avec la façon dont cron exécute les scripts car ils n'ont normalement pas la configuration de mon environnement. Existe-t-il un moyen d'invoquer bash (?) De la même manière que cron pour que je puisse tester les scripts avant de les installer?


Je suggérerais cette solution: unix.stackexchange.com/questions/27289/…
rudi

Prenant @gregseth un peu plus loin, j'ai donné cette solution: unix.stackexchange.com/questions/27289/…
Robert Brisita

Réponses:


385

Ajoutez ceci à votre crontab (temporairement):

* * * * * env > ~/cronenv

Après son exécution, procédez comme suit:

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

Cela suppose que votre cron exécute / bin / sh, qui est la valeur par défaut indépendamment du shell par défaut de l'utilisateur.


5
Remarque: si vous ajoutez cela au fichier global / etc / crontab, vous aurez également besoin du nom d'utilisateur. Par exemple * * * * * root env> ~ / cronenv
Greg

10
Bonne idée simple. Pour l'impatience, utilisez '* * * * *' pour exécuter la minute suivante, et n'oubliez pas de désactiver à nouveau lorsque vous avez fini de jouer ;-)
Mads Buus

5
@Madsn Pour revenir au shell bash précédent, essayez: exit
spkane

5
L'importance de cette réponse ne peut être sous-estimée. Digne d'une inclusion de paragraphe dans un livre.
Xofo

1
est-ce que env - cat ~ / cronenv` / bin / sh` doit également être écrit comme tâche cron? veuillez donner un exemple
JavaSa

61

Cron fournit uniquement cet environnement par défaut:

  • HOME répertoire personnel de l'utilisateur
  • LOGNAME connexion de l'utilisateur
  • PATH=/usr/bin:/usr/sbin
  • SHELL=/usr/bin/sh

Si vous avez besoin de plus, vous pouvez source un script où vous définissez votre environnement avant la table de planification dans la crontab.


7
.ne fait généralement plus partie PATH, pour des raisons de sécurité .
l0b0

49

Quelques approches:

  1. Exportez cron env et sourcez-le:

    Ajouter

    * * * * * env > ~/cronenv

    à votre crontab, laissez-le fonctionner une fois, éteignez-le, puis exécutez

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

    Et vous êtes maintenant dans une shsession qui a l'environnement de cron

  2. Apportez votre environnement à cron

    Vous pouvez sauter l'exercice ci-dessus et faire juste . ~/.profiledevant votre tâche cron, par exemple

    * * * * * . ~/.profile; your_command
  3. Utiliser l'écran

    Au-dessus de deux solutions échouent toujours en ce qu'elles fournissent un environnement connecté à une session X en cours d'exécution, avec accès à dbusetc. Par exemple, sur Ubuntu, nmcli(Network Manager) fonctionnera dans les deux approches ci-dessus, mais échouera toujours dans cron.

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

    Ajoutez la ligne ci-dessus au cron, laissez-la fonctionner une fois, désactivez-la. Connectez-vous à votre session écran (screen -r). Si vous vérifiez que la session écran a été créée (avec ps) sachez qu’elles sont parfois en majuscules (par exemple ps | grep SCREEN)

    Maintenant, même nmcliet similaire échouera.


22

Tu peux courir:

env - your_command arguments

Cela exécutera votre_commande avec un environnement vide.


4
cron ne fonctionne pas dans un environnement complètement vide, n'est-ce pas?
jldupont

2
gregseth a identifié les variables incluses dans l'environnement par cron. Vous pouvez inclure ces variables sur la ligne de commande. $ env - PATH = "$ PATH" command args
DragonFax

4
@DragonFax @dimba J'utilise env - HOME="$HOME" LOGNAME="$USER" PATH="/usr/bin:/bin" SHELL="$(which sh)" command argumentsce qui semble faire l'affaire
l0b0

Il s'agit d'une excellente méthode simple pour tester des scripts dans des environnements "hostiles" ou inconnus. Si vous êtes suffisamment explicite pour qu'il s'exécute ici, il s'exécutera sous cron.
Oli


13

Réponse six ans plus tard: le problème de l'inadéquation de l'environnement est l'un des problèmes résolus par systemd "timers" en remplacement de cron. Que vous exécutiez le "service" systemd à partir de la CLI ou via cron, il reçoit exactement le même environnement, évitant ainsi le problème de non-concordance de l'environnement.

Le problème le plus courant pour provoquer l'échec des tâches cron lorsqu'elles passent manuellement est la valeur $PATHpar défaut restrictive définie par cron, qui est celle-ci sur Ubuntu 16.04:

"/usr/bin:/bin"

En revanche, la valeur $PATHpar défaut définie systemdsur Ubuntu 16.04 est:

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

Il y a donc déjà une meilleure chance qu'un temporisateur systemd puisse trouver un binaire sans autre tracas.

L'inconvénient avec les minuteries systemd, c'est qu'il y a un peu plus de temps pour les configurer. Vous créez d'abord un fichier "service" pour définir ce que vous voulez exécuter et un fichier "timer" pour définir le planning d'exécution et enfin "activez" le timer pour l'activer.


10

Créez un travail cron qui exécute env et redirige stdout vers un fichier. Utilisez le fichier à côté de "env -" pour créer le même environnement qu'une tâche cron.


Désolé, cela m'a dérouté. "env - script" n'est-il pas suffisant?
Jorge Vargas

Cela vous donnera un environnement vide. Lorsque vous exécutez des scripts via cron, l'environnement n'est pas vide.
Jens Carlberg


2

Par défaut, cronexécute ses travaux en utilisant l'idée de votre système sh. Cela pourrait être le shell Bourne réel ou dash, ash, kshou bash(ou un autre) un lien symbolique pour sh(et par conséquent l' exécution en mode POSIX).

La meilleure chose à faire est de vous assurer que vos scripts ont ce dont ils ont besoin et de supposer que rien n'est prévu pour eux. Par conséquent, vous devez utiliser les spécifications de répertoire complètes et définir des variables d'environnement telles que $PATHvous.


C'est exactement ce que j'essaie de résoudre. Nous avons des tonnes de problèmes avec les scripts qui supposent quelque chose par erreur. Faire des chemins complets et définir des variables env et toutes les ordures se retrouve avec d'horribles énormes lignes cron incontrôlables
Jorge Vargas

re * sh désolé d'avoir grandi avec bash = shell donc il m'est difficile de me souvenir des alternatives (et parfois mieux) des shells.
Jorge Vargas

1
@Jorge: les lignes de la crontab doivent être assez courtes. Vous devez effectuer toute la configuration dont vous avez besoin dans le script (ou un script wrapper). Voici une ligne typique d'un crontab comme exemple: 0 0 * * 1 /path/to/executable >/dev/null 2>&1puis, dans "exécutable", je définirais des valeurs pour $PATH, etc., et utiliserais les spécifications complètes du répertoire pour entrer et sortir des fichiers, etc. Par exemple:/path/to/do_something /another/path/input_file /another/path/to/output_file
Pause jusqu'à nouvel ordre.

1

Un autre moyen simple que j'ai trouvé (mais qui peut être sujet à des erreurs, je suis toujours en train de tester) est de source les fichiers de profil de votre utilisateur avant votre commande.

Modification d'un script /etc/cron.d/:

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

Deviendrait:

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

Sale, mais ça a fait le travail pour moi. Existe-t-il un moyen de simuler une connexion? Juste une commande que vous pourriez exécuter?bash --loginn'a pas fonctionné. Il semble que ce serait la meilleure façon de procéder.

EDIT: Cela semble être une solution solide: 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 réponse acceptée donne un moyen d'exécuter un script avec l'environnement que cron utiliserait. Comme d'autres l'ont souligné, ce n'est pas le seul critère nécessaire pour déboguer les tâches cron.

En effet, cron utilise également un terminal non interactif, sans entrée attachée, etc.

Si cela aide, j'ai écrit un script qui permet d'exécuter sans douleur une commande / script comme il serait exécuté par cron. Invoquez-le avec votre commande / script comme premier argument et vous êtes bon.

Ce script est également hébergé (et éventuellement mis à jour) sur 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 réponse https://stackoverflow.com/a/2546509/5593430 montre comment obtenir l'environnement cron et l'utiliser pour votre script. Mais sachez que l'environnement peut différer selon le fichier crontab que vous utilisez. J'ai créé trois entrées cron différentes pour sauvegarder l'environnement via env > log. Ce sont les résultats sur un Amazon Linux 4.4.35-33.55.amzn1.x86_64.

1. Global / etc / crontab avec l'utilisateur 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. Utilisateur 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 dans /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

Plus important encore PATH, PWDet HOMEdiffèrent. Assurez-vous de les définir dans vos scripts cron pour qu'ils reposent sur un environnement stable.


-8

Je ne pense pas qu'il y en ait; le seul moyen que je connaisse pour tester un travail cron est de le configurer pour qu'il s'exécute une minute ou deux à l'avenir, puis d'attendre.


C'est pourquoi je demande simuler
Jorge Vargas
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.