Controlla se lo script è avviato da cron, piuttosto che invocato manualmente


23

C'è qualche variabile che cron imposta quando esegue un programma? Se lo script è gestito da cron, vorrei saltare alcune parti; altrimenti invocare quelle parti.

Come posso sapere se lo script Bash è avviato da cron?


Perché non solo noi ps?
terdon


@terdon: probabilmente perché psè abbastanza male documentato (specialmente la versione di Linux che supporta diversi stili di sintassi) e la pagina man è ancora più densa e criptica della maggior parte degli strumenti. Sospetto che la maggior parte delle persone non si renda conto di quanto pspossa essere utile e versatile uno strumento .
Caso

Risposte:


31

Non sono a conoscenza del fatto che cronqualsiasi cosa al suo ambiente per impostazione predefinita possa essere utile qui, ma ci sono un paio di cose che potresti fare per ottenere l'effetto desiderato.

1) Creare un collegamento hard o soft al file di script, in modo che, ad esempio, myscripte myscript_via_cronpuntare allo stesso file. È quindi possibile verificare il valore di $0all'interno dello script quando si desidera eseguire in modo condizionale o omettere determinate parti del codice. Inserisci il nome appropriato nel tuo crontab e sei pronto.

2) Aggiungere un'opzione allo script e impostarla nell'invocazione crontab. Ad esempio, aggiungi un'opzione -c, che dice allo script di eseguire o omettere le parti appropriate del codice e aggiungi -cal nome del comando nel tuo crontab.

E, naturalmente, cron può impostare variabili di ambiente arbitrarie, quindi puoi semplicemente inserire una linea come RUN_BY_CRON="TRUE"nel tuo crontab e verificarne il valore nello script.


7
+1 per RUN_BY_CRON = true
cas

la risposta di cas funziona molto bene e può essere usata anche per qualsiasi altra cosa
Deian,

19

Gli script eseguiti da cron non vengono eseguiti in shell interattive. Né sono script di avvio. La differenziazione è che le shell interattive hanno STDIN e STDOUT collegati a un tty.

Metodo 1: controlla se $-include la ibandiera. iè impostato per shell interattive.

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

Metodo 2: verifica $PS1è vuoto.

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

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

Metodo 3: prova il tuo tty. non è così affidabile, ma per semplici lavori cron dovresti essere ok, poiché cron non alloca di default un tty a uno script.

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

Tieni presente che puoi comunque forzare l'uso di una shell interattiva -i, ma probabilmente saresti consapevole se lo stessi facendo ...


1
Si noti che il comando $ PS1 non funziona quando si controlla se lo script è avviato da systemd o meno. il $ - uno fa
mveroone il

1
Il link alla tua Winnipeg University è interrotto.
WinEunuuchs2Unix

1
@TimKennedy Prego .... da Edmonton :)
WinEunuuchs2Unix

'case "$ -" in' non sembra funzionare negli script bash.
Hobadee,

@Hobadee - ogni cosa a cui bashho accesso ha $ -, così come dashe ksh. anche le shell ristrette in Solaris ce l'hanno. Quale piattaforma stai cercando di usarlo dove non funziona? Cosa case "$-" in *i*) echo true ;; *) echo false ;; esacti mostra?
Tim Kennedy,

7

Innanzitutto, ottieni il PID di cron, quindi ottieni il PID (PPID) del processo corrente e confrontali:

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

Se lo script viene avviato da un altro processo che potrebbe essere stato avviato da cron, è possibile risalire i PID principali fino a quando non si arriva a $ CRONPID o 1 (PID di init).

qualcosa del genere, forse (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

Da Deian: questa è una versione testata su RedHat Linux

# 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}"

1
Su Solaris cron avvia una shell e la shell esegue lo script, che a sua volta avvia un'altra shell. Quindi il pid padre nello script non è il pid di cron.
ceving

4

Se il tuo file di script è invocato da crone contiene una shell nella prima riga come #!/bin/bashse fosse necessario trovare il nome genitore-genitore per il tuo scopo.

1) cronviene richiamato in un determinato momento nel tuo crontab, eseguendo una shell 2) shell esegue il tuo script 3) il tuo script è in esecuzione

Il PID padre è disponibile in bash come variabile $PPID. Il pscomando per ottenere il PID padre del PID padre è:

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

ma abbiamo bisogno del nome del comando, non del pid, quindi chiamiamo

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

ora non ci resta che testare il risultato per "cron"

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

Ora puoi testare ovunque nel tuo script

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

In bocca al lupo!


Questo funziona solo per Linux ps. Per MacOS (oltre a Linux, forse anche * BSD), puoi usare il seguente P_COMMAND:P_COMMAND=$(basename -a $(ps h -o comm $PPPID))
mdd

1

Funziona su FreeBSD o su 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

Puoi andare fino all'albero del processo che desideri.


1

Una soluzione generica alla domanda "è il mio output un terminale o sto correndo da uno script" è:

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

0

Un semplice echo $TERM | mail me@domain.comcron che mi ha mostrato che sia su Linux che su AIX, cron sembra impostato $TERMsu "stupido".

Ora teoricamente potrebbero esserci ancora dei veri e propri terminali stupidi in giro, ma sospetto che per la maggior parte delle occasioni, ciò sia sufficiente ...


0

Non esiste una risposta autorevole, ma le variabili prompt ( $PS1) e terminal ( $TERM) sono abbastanza decenti qui. Alcuni sistemi vengono impostati TERM=dumbmentre la maggior parte lo lascia vuoto, quindi controlleremo solo per uno:

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

Il codice precedente sostituisce la parola "stupido" quando non vi è alcun valore per $TERM. Pertanto, il condizionale si attiva quando non c'è $TERMo $TERMè impostato su "stupido" o se la $PS1variabile non è vuota.

Ho provato questo su Debian 9 ( TERM=), CentOS 6.4 e 7.4 ( TERM=dumb) e FreeBSD 7.3 ( TERM=).

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.