Compruebe si cron inicia el script, en lugar de invocarlo manualmente

23

¿Hay alguna variable que cron establece cuando ejecuta un programa? Si cron ejecuta el script, me gustaría omitir algunas partes; de lo contrario invoque esas partes.

¿Cómo puedo saber si el script Bash es iniciado por cron?

margarita
fuente
¿Por qué no solo nosotras ps?
terdon
@terdon: probablemente porque psestá bastante mal documentado (especialmente la versión de Linux que admite varios estilos de sintaxis diferentes) y la página de manual es aún más densa y críptica que la mayoría de las herramientas. Sospecho que la mayoría de las personas ni siquiera se dan cuenta de cuán útil y versátil pspuede ser una herramienta .
cas

Respuestas:

31

No soy consciente de que cronhace algo en su entorno por defecto que puede ser útil aquí, pero hay un par de cosas que podría hacer para obtener el efecto deseado.

1) Haga un enlace fijo o suave al archivo de script, de modo que, por ejemplo, myscripty myscript_via_cronapunte al mismo archivo. A continuación, puede probar el valor $0dentro del script cuando desea ejecutar u omitir condicionalmente ciertas partes del código. Pon el nombre apropiado en tu crontab y listo.

2) Agregue una opción al script y establezca esa opción en la invocación crontab. Por ejemplo, agregue una opción -c, que le dice al script que ejecute u omita las partes apropiadas del código, y agregue -cal nombre del comando en su crontab.

Y, por supuesto, cron puede establecer variables de entorno arbitrarias, por lo que podría poner una línea como RUN_BY_CRON="TRUE"en su crontab y verificar su valor en su script.

D_Bye
fuente
77
+1 para RUN_BY_CRON = verdadero
cas
la respuesta de cas funciona muy bien y también se puede utilizar para cualquier otra cosa
Deian
19

Los scripts ejecutados desde cron no se ejecutan en shells interactivos. Tampoco los scripts de inicio. La diferencia es que los shells interactivos tienen STDIN y STDOUT unidos a un tty.

Método 1: verifique si $-incluye la ibandera. iestá configurado para shells interactivos.

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

Método 2: el cheque $PS1está vacío.

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

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

Método 3: prueba tu tty. no es tan confiable, pero para trabajos cron simples debería estar bien, ya que cron no asigna por defecto un tty a un script.

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

Tenga en cuenta que, sin embargo, puede forzar el uso de un shell interactivo -i, pero probablemente lo sepa si lo hiciera ...

Tim Kennedy
fuente
1
Tenga en cuenta que el comando $ PS1 no funciona cuando se verifica si el script es iniciado por systemd o no. the $ - one does
mveroone
1
Su enlace de la Universidad de Winnipeg está roto.
WinEunuuchs2Unix
1
@TimKennedy De nada ... de Edmonton :)
WinEunuuchs2Unix
'case "$ -" in' no parece funcionar en scripts de bash.
Hobadee
@Hobadee: cada uno al que bashtengo acceso tiene $ -, al igual que dashy ksh. incluso las conchas restringidas en Solaris lo tienen. ¿Qué plataforma estás tratando de usar cuando no funciona? ¿Qué case "$-" in *i*) echo true ;; *) echo false ;; esacte muestra?
Tim Kennedy
7

Primero, obtenga el PID de cron, luego obtenga el PID principal (PPID) del proceso actual y compárelos:

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

Si su secuencia de comandos es iniciada por otro proceso que podría haber sido iniciado por cron, entonces puede volver a subir los PID principales hasta llegar a $ CRONPID o 1 (PID de init).

algo como esto, tal vez (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

De Deian: esta es una versión probada en 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}"
cas
fuente
1
En Solaris, cron inicia un shell y el shell ejecuta el script, que a su vez inicia otro shell. Entonces el padre pid en el script no es el pid de cron.
ceving
4

Si su archivo de script es invocado por crony contiene un shell en la primera línea como #!/bin/bashsi necesitara encontrar el nombre padre-padre para su propósito.

1) cronse invoca en el momento dado en su crontab, ejecutando un shell 2) el shell ejecuta su script 3) su script se está ejecutando

El PID principal está disponible en bash como variable $PPID. El pscomando para obtener el PID primario del PID primario es:

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

pero necesitamos el nombre del comando, no el pid, así que llamamos

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

ahora solo necesitamos probar el resultado para "cron"

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

Ahora puedes probar en cualquier parte de tu script

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

¡Buena suerte!

Olray
fuente
Esto funciona solo para Linux ps. Para MacOS (así como Linux, tal vez * BSD también), puede usar el siguiente P_COMMAND:P_COMMAND=$(basename -a $(ps h -o comm $PPPID))
mdd
1

Funciona en FreeBSD o en 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

Puede ir tan lejos en el árbol de procesos como lo desee.

Ted Rypma
fuente
1

Una solución genérica a la pregunta "es mi salida un terminal o estoy ejecutando desde un script" es:

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

Un simple echo $TERM | mail [email protected]en cron me mostró que tanto en Linux como en AIX, cron parece estar configurado $TERMcomo 'tonto'.

Ahora, teóricamente, todavía puede haber terminales tontos reales, pero sospecho que para la mayoría de las ocasiones, eso debería ser suficiente ...

vegivamp
fuente
0

No hay una respuesta autorizada, pero las variables prompt ( $PS1) y terminal ( $TERM) son bastante decentes aquí. Algunos sistemas se configuran TERM=dumbmientras que la mayoría lo deja vacío, por lo que solo buscaremos:

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

El código anterior sustituye la palabra "tonto" cuando no hay valor para $TERM. Por lo tanto, el condicional se activa cuando no hay $TERMo $TERMse establece en "tonto" o si la $PS1variable no está vacía.

He probado esto en Debian 9 ( TERM=), CentOS 6.4 y 7.4 ( TERM=dumb) y FreeBSD 7.3 ( TERM=).

Adam Katz
fuente