Wie simuliert man die Umgebung, mit der cron ein Skript ausführt?

253

Normalerweise habe ich verschiedene Probleme mit der Ausführung von Skripten durch cron, da meine Umgebung normalerweise nicht eingerichtet ist. Gibt es eine Möglichkeit, bash (?) Auf die gleiche Weise wie cron aufzurufen, damit ich Skripte testen kann, bevor ich sie installiere?

Jorge Vargas
quelle
Ich würde diese Lösung vorschlagen: unix.stackexchange.com/questions/27289/…
rudi
Als ich @gregseth etwas weiter ging, gab ich diese Lösung: unix.stackexchange.com/questions/27289/…
Robert Brisita

Antworten:

385

Fügen Sie dies Ihrer Crontab hinzu (vorübergehend):

* * * * * env > ~/cronenv

Führen Sie nach dem Ausführen folgende Schritte aus:

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

Dies setzt voraus, dass Ihr Cron / bin / sh ausführt. Dies ist die Standardeinstellung, unabhängig von der Standard-Shell des Benutzers.

mmccoo
quelle
5
Hinweis: Wenn Sie dies zur globalen / etc / crontab hinzufügen, benötigen Sie auch den Benutzernamen. ZB * * * * * root env> ~ / cronenv
Greg
10
Gute, einfache Idee. Für die ungeduldigen verwenden Sie '* * * * *', um in der nächsten Minute zu laufen, und denken Sie daran, wieder auszuschalten, wenn Sie mit dem Spielen fertig sind ;-)
Mads Buus
5
@Madsn Um zur vorherigen Bash-Shell zurückzukehren, versuchen Sie: exit
spkane
5
Die Bedeutung dieser Antwort ist nicht zu unterschätzen. Eine Absatzaufnahme in ein Buch verdient.
Xofo
1
soll env - cat ~ / cronenv` / bin / sh` auch als cron job geschrieben werden? Bitte geben Sie ein Beispiel
JavaSa
61

Cron bietet standardmäßig nur diese Umgebung:

  • HOME Home-Verzeichnis des Benutzers
  • LOGNAME Benutzer Login
  • PATH=/usr/bin:/usr/sbin
  • SHELL=/usr/bin/sh

Wenn Sie mehr benötigen, können Sie ein Skript erstellen, in dem Sie Ihre Umgebung vor der Planungstabelle in der Crontab definieren.

gregseth
quelle
7
.ist PATHaus Sicherheitsgründen normalerweise nicht mehr Teil von .
10.
49

Einige Ansätze:

  1. Exportieren Sie cron env und beziehen Sie es:

    Hinzufügen

    * * * * * env > ~/cronenv

    Lassen Sie es einmal laufen, schalten Sie es wieder aus und laufen Sie dann zu Ihrem Crontab

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

    Und Sie befinden sich jetzt in einer shSitzung mit Cron-Umgebung

  2. Bringen Sie Ihre Umgebung zu cron

    Sie könnten über die Übung hinausgehen und einfach einen . ~/.profileJob vor Ihrem Cron machen, z

    * * * * * . ~/.profile; your_command
  3. Bildschirm verwenden

    Die beiden oben genannten Lösungen schlagen immer noch fehl, da sie eine Umgebung bereitstellen, die mit einer laufenden X-Sitzung verbunden ist, auf die dbususw. zugegriffen werden kann. Unter Ubuntu nmclifunktioniert (Network Manager) beispielsweise in den beiden oben genannten Ansätzen, schlägt jedoch in cron immer noch fehl.

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

    Fügen Sie die obige Zeile zu cron hinzu, lassen Sie es einmal laufen und schalten Sie es wieder aus. Stellen Sie eine Verbindung zu Ihrer Bildschirmsitzung her (Bildschirm -r). Wenn Sie die Bildschirm - Sitzung Überprüfung erstellt wurde (mit ps) bewusst sein , dass sie manchmal in Großbuchstaben (zB ps | grep SCREEN)

    Jetzt wird sogar nmcliund ähnlich scheitern.

Plätzchen
quelle
22

Du kannst rennen:

env - your_command arguments

Dadurch wird Ihr Befehl mit einer leeren Umgebung ausgeführt.

dimba
quelle
4
cron läuft nicht in einer völlig leeren Umgebung, oder?
Jldupont
2
Gregseth identifizierte die in der Umgebung enthaltenen Variablen nach Cron. Sie können diese Variablen in die Befehlszeile aufnehmen. $ env - PATH = "$ PATH" Befehl args
DragonFax
4
@DragonFax @dimba Ich benutze, env - HOME="$HOME" LOGNAME="$USER" PATH="/usr/bin:/bin" SHELL="$(which sh)" command argumentswas den Trick zu tun scheint
l0b0
Dies ist eine großartige einfache Methode zum Testen von Skripten in "feindlichen" oder unbekannten Umgebungen. Wenn Sie explizit genug sind, dass es in diesem ausgeführt wird, wird es unter cron ausgeführt.
Oli
13

Antwort sechs Jahre später: Das Problem der Nichtübereinstimmung der Umgebung ist eines der Probleme, die von systemd"Timern" als Cron-Ersatz gelöst werden . Unabhängig davon, ob Sie den systemd "Service" über die CLI oder über cron ausführen, erhält er genau dieselbe Umgebung, wodurch das Problem der Nichtübereinstimmung der Umgebung vermieden wird.

Das häufigste Problem, das dazu führt, dass Cron-Jobs fehlschlagen, wenn sie manuell übergeben werden, ist der $PATHvon cron festgelegte restriktive Standard , der unter Ubuntu 16.04 verwendet wird:

"/usr/bin:/bin"

Im Gegensatz dazu ist der unter Ubuntu 16.04 $PATHfestgelegte Standard systemd:

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

Es besteht also bereits eine bessere Chance, dass ein Systemd-Timer ohne weiteren Aufwand eine Binärdatei findet.

Der Nachteil bei systemd-Timern ist, dass etwas mehr Zeit zum Einrichten bleibt. Sie erstellen zuerst eine "Service" -Datei, um zu definieren, was Sie ausführen möchten, und eine "Timer" -Datei, um den Zeitplan für die Ausführung zu definieren, und "aktivieren" schließlich den Timer, um ihn zu aktivieren.

Mark Stosberg
quelle
10

Erstellen Sie einen Cron-Job, der env ausführt und stdout in eine Datei umleitet. Verwenden Sie die Datei neben "env -", um dieselbe Umgebung wie ein Cron-Job zu erstellen.

Jens Carlberg
quelle
Entschuldigung, das hat mich verwirrt. ist "env - script" nicht genug?
Jorge Vargas
Das gibt Ihnen eine leere Umgebung. Wenn Sie Skripte über cron ausführen, ist die Umgebung nicht leer.
Jens Carlberg
3

Vergessen Sie nicht, dass crons übergeordnetes Element init ohne steuerndes Terminal ausführt. Sie können dies mit einem Tool wie diesem simulieren:

http://libslack.org/daemon/

Randy Proctor
quelle
2

Standardmäßig werden crondie Jobs mit den Vorstellungen Ihres Systems ausgeführt sh. Dies könnte die tatsächliche Bourne - Shell oder sein dash, ash, kshoder bash(oder ein anderer) symlinked zu sh(und als Folge in POSIX - Modus ausgeführt wird ).

Am besten stellen Sie sicher, dass Ihre Skripte das haben, was sie benötigen, und nehmen an, dass nichts für sie bereitgestellt wird. Daher sollten Sie vollständige Verzeichnisspezifikationen verwenden und Umgebungsvariablen wie Sie $PATHselbst festlegen .

Bis auf weiteres angehalten.
quelle
Genau das versuche ich zu lösen. Wir bekommen Unmengen von Problemen mit Skripten, die versehentlich etwas annehmen. Wenn Sie vollständige Pfade ausführen und env-Variablen und den gesamten Müll festlegen, erhalten Sie schreckliche, riesige, nicht wartbare Cron-Linien
Jorge Vargas,
Es tut mir leid, dass ich mit bash = shell aufgewachsen bin, daher fällt es mir schwer, mich an die alternativen (und manchmal besseren) Shells zu erinnern.
Jorge Vargas
1
@Jorge: Linien in der Crontab sollten ziemlich kurz sein. Sie sollten alle erforderlichen Einstellungen innerhalb des Skripts (oder eines Wrapper-Skripts) vornehmen. Hier ist eine typische Zeile aus einer Crontab als Beispiel: 0 0 * * 1 /path/to/executable >/dev/null 2>&1und dann würde ich innerhalb von "ausführbar" Werte für $PATHusw. festlegen und vollständige Verzeichnisspezifikationen zum Eingeben und /path/to/do_something /another/path/input_file /another/path/to/output_file
Ausgeben von
1

Eine andere einfache Möglichkeit, die ich gefunden habe (die jedoch möglicherweise fehleranfällig ist und die ich noch teste), besteht darin, die Profildateien Ihres Benutzers vor Ihrem Befehl zu erstellen.

Bearbeiten eines /etc/cron.d/-Skripts:

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

Würde sich verwandeln in:

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

Schmutzig, aber es hat den Job für mich erledigt. Gibt es eine Möglichkeit, ein Login zu simulieren? Nur ein Befehl, den Sie ausführen könnten?bash --loginhat nicht funktioniert. Es klingt so, als wäre das der bessere Weg.

EDIT: Dies scheint eine solide Lösung zu sein: 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
quelle
1

Die akzeptierte Antwort bietet eine Möglichkeit, ein Skript mit der Umgebung auszuführen, die cron verwenden würde. Wie andere betonten, ist dies nicht das einzige erforderliche Kriterium für das Debuggen von Cron-Jobs.

In der Tat verwendet cron auch ein nicht interaktives Terminal ohne angeschlossenen Eingang usw.

Wenn das hilft, habe ich ein Skript geschrieben, mit dem ein Befehl / Skript schmerzlos ausgeführt werden kann, wie es von cron ausgeführt wird. Rufen Sie es mit Ihrem Befehl / Skript als erstem Argument auf und Sie sind gut.

Dieses Skript wird auch auf Github gehostet (und möglicherweise aktualisiert) .

#!/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
quelle
0

Die Antwort https://stackoverflow.com/a/2546509/5593430 zeigt, wie Sie die Cron-Umgebung abrufen und für Ihr Skript verwenden. Beachten Sie jedoch, dass die Umgebung je nach verwendeter Crontab-Datei unterschiedlich sein kann. Ich habe drei verschiedene Cron-Einträge erstellt, um die Umgebung über zu retten env > log. Dies sind die Ergebnisse unter Amazon Linux 4.4.35-33.55.amzn1.x86_64.

1. Global / etc / crontab mit Root-Benutzer

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. Benutzer crontab von 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. Skript 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

Am wichtigsten ist PATH,PWD und HOMEunterscheiden. Stellen Sie sicher, dass Sie diese in Ihren Cron-Skripten festlegen, um eine stabile Umgebung zu gewährleisten.

Maikel
quelle
-8

Ich glaube nicht, dass es das gibt; Die einzige Möglichkeit, einen Cron-Job zu testen, besteht darin, ihn so einzurichten, dass er in Zukunft ein oder zwei Minuten lang ausgeführt wird, und dann zu warten.

jn80842
quelle
Deshalb frage ich simulieren
Jorge Vargas