Verifique se o script é iniciado pelo cron, em vez de ser chamado manualmente

23

Existe alguma variável que o cron define quando executa um programa? Se o script for executado pelo cron, eu gostaria de pular algumas partes; caso contrário, invoque essas partes.

Como posso saber se o script Bash é iniciado pelo cron?

margarida
fonte
Por que você não apenas nós ps?
terdon
@terdon: provavelmente porque psestá bastante mal documentado (especialmente a versão do Linux que suporta vários estilos de sintaxe) e a página de manual é ainda mais densa e enigmática do que a maioria das ferramentas. Eu suspeito que a maioria das pessoas nem percebe o quão útil e versátil uma ferramenta pspode ser.
cas

Respostas:

31

Não sei que cron, por padrão, qualquer coisa que faça em seu ambiente possa ser útil aqui, mas há algumas coisas que você pode fazer para obter o efeito desejado.

1) Faça um link duro ou mole para o arquivo de script, de modo que, por exemplo, myscripte myscript_via_cronapontam para o mesmo arquivo. Em seguida, você pode testar o valor de $0dentro do script quando desejar executar ou omitir condicionalmente certas partes do código. Coloque o nome apropriado no seu crontab e pronto.

2) Adicione uma opção ao script e defina essa opção na chamada do crontab. Por exemplo, adicione uma opção -cque diga ao script para executar ou omitir as partes apropriadas do código e adicione -cao nome do comando no seu crontab.

E, é claro, o cron pode definir variáveis ​​de ambiente arbitrárias, para que você possa colocar uma linha como RUN_BY_CRON="TRUE"no seu crontab e verificar seu valor no seu script.

D_Bye
fonte
7
+1 para RUN_BY_CRON = true
cas
a resposta por cas está funcionando muito bem e pode ser usado para qualquer outra coisa também
Deian
19

Os scripts executados a partir do cron não são executados em shells interativos. Nem os scripts de inicialização. A diferenciação é que os shells interativos têm STDIN e STDOUT anexados a um tty.

Método 1: verifique se $-inclui o isinalizador. iestá definido para conchas interativas.

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

Método 2: verifique se $PS1está vazio.

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

referência: http://techdoc.kvindesland.no/linux/gnubooks/bash/bashref_54.html

Método 3: teste seu tty. não é tão confiável, mas para tarefas simples do cron, você deve estar bem, pois o cron não aloca um tty por padrão para um script.

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

No entanto, lembre-se de que você pode forçar um shell interativo -i, mas provavelmente saberia se estivesse fazendo isso ...

Tim Kennedy
fonte
1
Observe que o comando $ PS1 não funciona ao verificar se o script foi iniciado pelo systemd ou não. o $ - um faz
mveroone
1
Seu link da Universidade de Winnipeg está quebrado.
WinEunuuchs2Unix 31/12/16
1
@TimKennedy De nada .... de Edmonton :)
WinEunuuchs2Unix
'case "$ -" in' não parece funcionar em scripts bash.
Hobadee 17/05/19
@ Hobadee - todos os bashque tenho acesso têm $ -, assim como dashe ksh. até as conchas restritas no Solaris o possuem. Qual plataforma você está tentando usá-lo onde não está funcionando? O que case "$-" in *i*) echo true ;; *) echo false ;; esacte mostra?
Tim Kennedy
7

Primeiro, obtenha o PID do cron, obtenha o PID pai (PPID) pai do processo atual e compare-os:

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

Se o seu script for iniciado por outro processo que pode ter sido iniciado pelo cron, você poderá percorrer os PIDs principais até chegar a $ CRONPID ou 1 (PID do init).

algo assim, talvez (Não testado, mas pode funcionar <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 é uma versão testada no 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
fonte
1
No Solaris, o cron inicia um shell e o shell executa o script, que inicia outro shell. Portanto, o pid pai no script não é o pid do cron.
ceving 15/12/16
4

Se o seu arquivo de script for chamado crone contiver um shell na primeira linha, #!/bin/bashvocê precisará encontrar o nome pai-pai para seu propósito.

1) croné invocado em um determinado momento no seu crontab, executando um shell 2) shell executa seu script 3) seu script está sendo executado

O PID pai está disponível no bash como variável $PPID. O pscomando para obter o PID pai do PID pai é:

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

mas precisamos do nome do comando, não do pid, então chamamos

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

agora só precisamos testar o resultado para "cron"

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

Agora você pode testar em qualquer lugar do seu script

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

Boa sorte!

Olray
fonte
Isso funciona apenas para o Linux ps. Para MacOS (assim como Linux, talvez também * BSD), você pode usar o seguinte P_COMMAND:P_COMMAND=$(basename -a $(ps h -o comm $PPPID))
mdd 11/11
1

Funciona no FreeBSD ou no 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

Você pode ir até a árvore do processo conforme desejar.

Ted Rypma
fonte
1

Uma solução genérica para a pergunta "é minha saída de um terminal ou estou executando a partir de um script" é:

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

Um simples echo $TERM | mail [email protected]no cron me mostrou que, no Linux e no AIX, o cron parece definido $TERMcomo 'burro'.

Agora, teoricamente, ainda pode haver terminais burros de verdade, mas suspeito que, na maioria das ocasiões, isso seja suficiente ...

vegivamp
fonte
0

Não há resposta autorizada, mas as variáveis prompt ( $PS1) e terminal ( $TERM) são bastante decentes aqui. Alguns sistemas configurados TERM=dumbenquanto a maioria o deixa vazio, portanto, basta verificar:

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

O código acima substitui a palavra "burro" quando não há valor para $TERM. Portanto, o condicional é acionado quando não existe $TERMou $TERMestá definido como "burro" ou se a $PS1variável não estiver vazia.

Eu testei isso no Debian 9 ( TERM=), CentOS 6.4 e 7.4 ( TERM=dumb) e FreeBSD 7.3 ( TERM=).

Adam Katz
fonte