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?

Jorge Vargas
la source
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.

mmccoo
la source
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.

Gregseth
la source
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.

Biscuit
la source
22

Tu peux courir:

env - your_command arguments

Cela exécutera votre_commande avec un environnement vide.

dimba
la source
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.

Mark Stosberg
la source
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.

Jens Carlberg
la source
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
3

N'oubliez pas que puisque le parent de cron est init, il exécute des programmes sans terminal de contrôle. Vous pouvez simuler cela avec un outil comme celui-ci:

http://libslack.org/daemon/

Randy Proctor
la source
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.

En pause jusqu'à nouvel ordre.
la source
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
four43
la source
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"
Daladim
la source
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.

Maikel
la source
-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.

jn80842
la source
C'est pourquoi je demande simuler
Jorge Vargas