Überprüfen Sie, ob das Skript von cron gestartet und nicht manuell aufgerufen wurde

23

Gibt es eine Variable, die Cron beim Ausführen eines Programms setzt? Wenn das Skript von cron ausgeführt wird, möchte ich einige Teile überspringen. Andernfalls rufen Sie diese Teile auf.

Woher weiß ich, ob das Bash-Skript von cron gestartet wurde?

Gänseblümchen
quelle
Warum gehst du nicht einfach zu uns ps?
Terdon
@terdon: wahrscheinlich, weil pses ziemlich schlecht dokumentiert ist (insbesondere die Linux-Version, die verschiedene Syntaxstile unterstützt) und die Manpage noch dichter und kryptischer ist als die meisten Tools. Ich vermute, die meisten Leute wissen nicht einmal, wie nützlich und vielseitig ein Werkzeug sein pskann.
cas

Antworten:

31

Mir ist nicht bewusst, dass crondies standardmäßig irgendetwas mit der Umgebung zu tun hat, was hier von Nutzen sein kann, aber es gibt ein paar Dinge, die Sie tun können, um den gewünschten Effekt zu erzielen.

1) Machen Sie einen harten oder weichen Link auf die Skriptdatei, so dass zum Beispiel myscriptund myscript_via_cronauf die gleiche Datei. Sie können dann den Wert $0innerhalb des Skripts testen, wenn Sie bestimmte Teile des Codes bedingt ausführen oder weglassen möchten. Geben Sie den entsprechenden Namen in Ihre Crontab ein, und schon sind Sie fertig.

2) Fügen Sie dem Skript eine Option hinzu, und legen Sie diese Option im Aufruf von crontab fest. Fügen Sie beispielsweise eine Option hinzu -c, die das Skript anweist, die entsprechenden Teile des Codes auszuführen oder wegzulassen, und fügen Sie -csie dem Befehlsnamen in Ihrer crontab hinzu.

Und natürlich cron können beliebige Umgebungsvariablen gesetzt, so dass Sie nur eine Zeile wie setzen könnte RUN_BY_CRON="TRUE"in Ihre crontab, und prüfen Sie seinen Wert in Ihrem Skript.

D_Bye
quelle
7
+1 für RUN_BY_CRON = true
cas
Die Antwort von cas funktioniert sehr gut und kann auch für alles andere verwendet werden
Deian
19

Skripte, die von cron ausgeführt werden, werden nicht in interaktiven Shells ausgeführt. Weder sind Startskripte. Die Unterscheidung besteht darin, dass interaktive Shells STDIN und STDOUT an ein tty angehängt haben.

Methode 1: Überprüfen Sie, ob $-das iFlag enthalten ist. iist auf interaktive Shells eingestellt.

case "$-" in
    *i*)
        interactive=1
        ;;
    *)
        not_interactive=1
        ;;
esac

Methode 2: Prüfung $PS1ist leer.

if [ -z "$PS1" ]; then
    not_interactive=1 
else
    interactive=1
fi

Referenz: http://techdoc.kvindesland.no/linux/gnubooks/bash/bashref_54.html

Methode 3: Testen Sie Ihre tty. Es ist nicht so zuverlässig, aber für einfache Cron-Jobs sollten Sie in Ordnung sein, da Cron einem Skript standardmäßig kein tty zuweist.

if [ -t 0 ]; then
    interactive=1
else
    non_interactive=1
fi

Denken Sie daran, dass Sie eine interaktive Shell mit erzwingen können -i, aber Sie wären sich wahrscheinlich bewusst, wenn Sie dies tun würden ...

Tim Kennedy
quelle
1
Beachten Sie, dass der Befehl $ PS1 nicht funktioniert, wenn Sie prüfen, ob das Skript von systemd gestartet wurde oder nicht. der $ - one does
mveroone
1
Ihr Winnipeg University Link ist defekt.
WinEunuuchs2Unix
1
@ TimKennedy Gern geschehen .... aus Edmonton :)
WinEunuuchs2Unix
'case "$ -" in' scheint in Bash-Skripten nicht zu funktionieren.
Hobadee
@Hobadee - jeder, auf den bashich Zugriff habe, hat $ -, genau wie dashund ksh. Sogar die eingeschränkten Shells in Solaris haben es. Auf welcher Plattform möchten Sie es verwenden, wenn es nicht funktioniert? Was case "$-" in *i*) echo true ;; *) echo false ;; esaczeigt es dir?
Tim Kennedy
7

Holen Sie sich zuerst die PID von cron, dann die übergeordnete PID (PPID) des aktuellen Prozesses und vergleichen Sie sie:

CRONPID=$(ps ho %p -C cron)
PPID=$(ps ho %P -p $$)
if [ $CRONPID -eq $PPID ] ; then echo Cron is our parent. ; fi

Wenn Ihr Skript von einem anderen Prozess gestartet wird, der möglicherweise von cron gestartet wurde, können Sie die übergeordneten PIDs zurückgehen, bis Sie entweder $ CRONPID oder 1 (Init's PID) erhalten.

so etwas vielleicht (Untested-But-It-Might-Work <TM>):

PPID=$$   # start from current PID
CRON_IS_PARENT=0
CRONPID=$(ps ho %p -C cron)
while [ $CRON_IS_PARENT -ne 1 ] && [ $PPID -ne 1 ] ; do
  PPID=$(ps ho %P -p $PPID)
  [ $CRONPID -eq $PPID ] && CRON_IS_PARENT=1
done

Von Deian: Dies ist eine Version, die unter RedHat Linux getestet wurde

# start from current PID
MYPID=$$
CRON_IS_PARENT=0
# this might return a list of multiple PIDs
CRONPIDS=$(ps ho %p -C crond)

CPID=$MYPID
while [ $CRON_IS_PARENT -ne 1 ] && [ $CPID -ne 1 ] ; do
        CPID_STR=$(ps ho %P -p $CPID)
        # the ParentPID came up as a string with leading spaces
        # this will convert it to int
        CPID=$(($CPID_STR))
        # now loop the CRON PIDs and compare them with the CPID
        for CRONPID in $CRONPIDS ; do
                [ $CRONPID -eq $CPID ] && CRON_IS_PARENT=1
                # we could leave earlier but it's okay like that too
        done
done

# now do whatever you want with the information
if [ "$CRON_IS_PARENT" == "1" ]; then
        CRON_CALL="Y"
else
        CRON_CALL="N"
fi

echo "CRON Call: ${CRON_CALL}"
cas
quelle
1
Unter Solaris startet cron eine Shell und die Shell führt das Skript aus, das selbst eine andere Shell startet. Die übergeordnete PID im Skript ist also nicht die PID von Cron.
15.
4

Wenn Ihre Skriptdatei von aufgerufen wird cronund eine Shell in der ersten Zeile enthält #!/bin/bash, müssen Sie den übergeordneten Namen für Ihren Zweck suchen.

1) cronwird zum angegebenen Zeitpunkt in Ihrem aufgerufen crontab, Ausführen einer Shell 2) Shell führt Ihr Skript aus 3) Ihr Skript wird ausgeführt

Die übergeordnete PID ist in bash als Variable verfügbar $PPID. Der psBefehl zum Abrufen der übergeordneten PID der übergeordneten PID lautet:

PPPID=`ps h -o ppid= $PPID`

aber wir brauchen den Namen des Befehls, nicht die PID, also rufen wir an

P_COMMAND=`ps h -o %c $PPPID`

Jetzt müssen wir nur noch das Ergebnis für "cron" testen

if [ "$P_COMMAND" == "cron" ]; then
  RUNNING_FROM_CRON=1
fi

Jetzt können Sie überall in Ihrem Skript testen

if [ "$RUNNING_FROM_CRON" == "1" ]; then
  ## do something when running from cron
else
  ## do something when running from shell
fi

Viel Glück!

Olray
quelle
Dies funktioniert nur für Linux ps. Für MacOS (und vielleicht auch für Linux * BSD) können Sie den folgenden P_COMMAND verwenden:P_COMMAND=$(basename -a $(ps h -o comm $PPPID))
mdd
1

Funktioniert unter FreeBSD oder Linux:

if [ "Z$(ps o comm="" -p $(ps o ppid="" -p $$))" == "Zcron" -o \
     "Z$(ps o comm="" -p $(ps o ppid="" -p $(ps o ppid="" -p $$)))" == "Zcron" ]
then
    echo "Called from cron"
else
    echo "Not called from cron"
fi

Sie können den Prozessbaum beliebig weit nach oben verschieben.

Ted Rypma
quelle
1

Eine generische Lösung für die Frage "Ist meine Ausgabe ein Terminal oder wird ein Skript ausgeführt?" Lautet:

( : > /dev/tty) && dev_tty_good=y || dev_tty_good=n
Stephen
quelle
0

Ein einfaches echo $TERM | mail [email protected]in cron hat mir gezeigt, dass cron sowohl unter Linux als auch unter AIX $TERMauf 'dumm' gesetzt ist.

Theoretisch gibt es vielleicht immer noch dumme Terminals, aber ich vermute, dass das für die meisten Gelegenheiten ausreichen sollte ...

vegivamp
quelle
0

Es gibt keine verbindliche Antwort, aber die Variablen prompt ( $PS1) und terminal ( $TERM) sind hier ziemlich anständig. Einige Systeme werden so eingestellt, dass TERM=dumbdie meisten leer bleiben. Wir prüfen daher nur, ob:

if [ "${TERM:-dumb}$PS1" != "dumb" ]; then
  echo "This is not a cron job"
fi

Der obige Code ersetzt das Wort "dumm", wenn es keinen Wert für gibt $TERM. Aus diesem Grund wird die Bedingung ausgelöst, wenn keine $TERModer $TERM"dumm" eingestellt ist oder wenn die $PS1Variable nicht leer ist.

Ich habe dies unter Debian 9 ( TERM=), CentOS 6.4 & 7.4 ( TERM=dumb) und FreeBSD 7.3 ( TERM=) getestet .

Adam Katz
quelle