Programar o último dia de cada mês

9

Eu li uma instrução para agendar um script no último dia do mês:

Note:
The astute reader might be wondering just how you would be able to set a command to execute on the last day of every month because you can’t set the dayofmonth value to cover every month. This problem has plagued Linux and Unix programmers, and has spawned quite a few different solutions. A common method is to add an if-then statement that uses the date command to check if tomorrow’s date is 01:

00 12 * * * if ['date +%d -d tomorrow' = 01 ] ; then ; command1

This checks every day at 12 noon to see if it's the last day of the month, and if so, cron runs the command.

Como o ['date +%d -d tomorrow' = 01 ] funciona?
É correto indicar then; command1 ?

    
por avirate 29.10.2018 / 03:59

2 respostas

15

Essa é uma citação do livro "Linux Command Line e Shell Scripting Bible", de Richard Blum, Christine Bresnahan pp 442, Terceira Edição, John Wiley & Filhos © 2015.

Sim, é o que diz, mas isso está errado / incompleto:

  • Faltando um fechamento fi .
  • Precisa de espaço entre [ e o seguinte ' .
  • É strongmente recomendado usar $ (…) em vez de '…' .
  • É importante que você use aspas em torno de expansões como "$(…)"
  • Existe um ; adicional após then

Como eu sei? (bem, pela experiência ☺), mas você pode tentar Shellcheck . Cole o código do livro (depois dos asteriscos) e ele mostrará os erros listados acima, além de um "shebang ausente". Um script sem erros no Shellcheck é este:

     #!/bin/sh if [ "$(date +%d -d tomorrow)" = 01 ] ; then script.sh; fi

Esse site funciona porque o que foi escrito é "código shell". Essa é uma sintaxe que funciona em muitos shells.

Alguns problemas que a verificação de shell não menciona são:

  • Está assumindo que o comando date é a versão de data do GNU. Aquele com uma opção -d que aceita tomorrow como um valor (busybox tem uma opção -d mas não entende amanhã e o BSD tem uma opção -d , mas não está relacionado a "exibição" de tempo). / p>

  • É melhor definir o formato após todas as opções date -d tomorrow +'%d' .

  • A hora de início do cron é sempre na hora local, o que pode fazer com que uma tarefa seja iniciada 1 hora antes ou depois de uma contagem exata se a hora de verão for definida ou desativada.

O que fizemos foi um script de shell que pode ser chamado com o cron. Podemos modificar ainda mais o script para aceitar argumentos do programa ou comando para executar, assim (finalmente, o código correto):

#!/bin/sh
[ "$#" -eq 0 ] && echo "Usage: $0 command [args]" && exit 1
[ "$(date -d tomorrow +'%d')" = 01 ] || exit 0
exec "$@"

Chame esse script end_of_month.sh e a chamada no cron é simplesmente:

00 12 28-31 * * /path/to/script/end_of_month.sh command

Isso executaria o script end_of_month (que internamente verificará se o dia é o último dia do mês) somente nos dias 28, 29, 30 e 31. Não é necessário verificar o final do mês em qualquer outro dia.

Verifique se o caminho correto está incluído. O PATH dentro do cron não (provavelmente) será o mesmo que o PATH do usuário.

Observe que há um script de fim de mês testado (conforme indicado abaixo) que pode chamar muitos outros utilitários ou scripts.

Isso também evitará o problema adicional que o cron gera com a linha de comando completa:

  • Cron divide a linha de comando em qualquer % , mesmo que seja citado com ' ou " (somente \ funciona aqui). Essa é uma maneira comum em que as tarefas do cron falham.

Você pode testar se o script end_of_month.sh funciona corretamente em alguma data (sem esperar até o final do mês para descobrir se ele não funciona) testando-o com faketime:

$ faketime 2018/10/31 ./end_of_month echo "Command will be executed...."
Command will be executed....
    
por 29.10.2018 / 10:26
8

Supondo que os erros de sintaxe são corrigidos e o comando reformulado ligeiramente para ser menos detalhado:

00 12 28-31 * * [ "$( date -d tomorrow +\%d )" != "01" ] || command1

Isso executa date +%d -d tomorrow (supondo que seja% GNUdate que é usado) para obter a data de amanhã como um número de dois dígitos. Se o número não for 01 , hoje não será o último dia do mês. Nesse caso, os testes são bem-sucedidos e command1 não é executada. O trabalho é executado ao meio-dia nos dias que poderiam ser o último dia do mês.

O comando original:

00 12 * * * if ['date +%d -d tomorrow' = 01 ] ; then ; command1

Isso tem alguns problemas:

  • Sem espaço após [ .
  • A ; diretamente após then .
  • % é especial nas especificações do cron job e deve ser escapado como \% (consulte man 5 crontab ).
  • Não há% finalfi no final que corresponda ao if .
por 29.10.2018 / 09:33