Tratamento de parâmetros do tipo DATAS dd/mm/YYYY em shell scripts [RESOLVIDO]

1. Tratamento de parâmetros do tipo DATAS dd/mm/YYYY em shell scripts [RESOLVIDO]

Newton Teixeira
NewtonJr.

(usa CentOS)

Enviado em 23/12/2015 - 18:11h

Prezados, preciso de ajuda para fazer o tratamento adequado de parâmetros do tipo data no formato dd/mm/YYYY fornecido pelo usuário do script na linha de comando. Tenho três scripts (diário, por intervalo de datas e mensal) que recebem parâmetros desse tipo.

OBS: Não precisam dizer como passar os parâmetros para as variáveis pois já fiz isso.

-> SCRIPT 01
./exec_scritps_cron_diario.sh --dt 01/04/2015 (exemplo)

Nesse script preciso testar se a data informada pelo usuário armazenada na variável $data é válida ou não.
Em caso negativo retornar um erro, Como fazer isso?

-> SCRIPT 02
./exec_scritps_cron_intervalo_datas.sh -dt1 01/04/2015 -dt2 30/04/2015 (exemplo)

Nesse script preciso testar se (as datas informadas pelo usuário são armazenadas nas variáveis $data_ini e $data_fim) :

* São datas válidas?
* A data armazenada em $data_fim é menor do que a data em $data_ini? Em caso negativo informar um erro. Como fazer isso?
* Se o usuário não digitar nenhuma data (Ex: ./exec_scritps_cron_intervalo_datas.sh), $data_ini e $data_fim deverão ser setadas com a data do primeiro e último dia do mês/ano atual do sistema. Como fazer isso?

-> SCRIPT 03
./exec_scripts_cron_mensal.sh -m 4 -n 2015

Nesse script preciso testar se (o mês e ano informadas pelo usuário armazenados nas variavéis $mes e $ano) :

* Se $mes e $ano possuem valores válidos para compor uma data. Como fazer esse teste?
* Variavéis $data_ini e $data_fim deverão ser setadas com a data do primeiro e último dia do mês/ano setado pelo usuário através das variavéis $mes e $ano. Como fazer?

Pessoal, obrigado pela atenção. Me ajudem porque isso são scripts que rodarão no servidor da empresa via crontab.

Oss!




  


2. Re: Tratamento de parâmetros do tipo DATAS dd/mm/YYYY em shell scripts [RESOLVIDO]

Paulo
paulo1205

(usa Ubuntu)

Enviado em 24/12/2015 - 05:31h

Em parte vai depender do shell que você utiliza no script. ksh e bash possuem mais operações internas do que sh, ash e dash.

Se você usar bash, pode primeiro fazer uma verificação básica de formato, que exige dias entre 01 e 31, meses entre 01 e 12 e ano com quatro dígitos, separados entre si com uma barra ("/").

if ! [[ "$date" =~ ^(0[1-9]|[12][0-9]|3[01])/(0[1-9]|1[0-2])/([0-9]{4})$ ]]; then
echo "Data inválida" >&2
exit 1
fi


Esse teste elimina erros crassos como caracteres inválidos numa data, dias e meses especificados como 00 ou valores absurdos, como 50. Mas não elimina ainda coisas como "30/02/2015" ou "31/09/1999", embora elimine "32/12/2015".

Convenientemente, quando se faz uma operação com o comparador =~ o bash coloca as subexpressões entre parênteses no array BASH_REMATCH, o que é conveniente para pegar o dia, o mês e o ano, para prosseguir com a análise.

year=${BASH_REMATCH[3]}
month=${BASH_REMATCH[2]}
day=${BASH_REMATCH[1]}


Você pode, então, testar se os valores de year, month e day fazem sentido quando testados em conjunto. Um teste obvio é observar se o ano é bissexto para saber se o dia 29 de fevereiro é válido ou não.

Um jeito rápido de fazer esses testes é chamar o comando date com a opção -d, passando a data no formato "$year/$month/$day". Se o formato for inválido, ele vai reclamar e vai sair indicando erro.

if ! date -d "$year/$month/$day" >/dev/null 2>&1; then
echo "Data inválida" >&2
exit 1
fi



3. Re: Tratamento de parâmetros do tipo DATAS dd/mm/YYYY em shell scripts [RESOLVIDO]

Perfil removido
removido

(usa Nenhuma)

Enviado em 24/12/2015 - 06:17h

Você pode usar o truque do comando echo com o comando sed:

TESTE=$(echo $DATA | sed 's/REGEX'//g)

O comando acima verifica se uma determinada data corresponde a uma determinada expressão regular.
Se corresponder, então a substituição é perfeita. Mas substituir o quê?
Substituir a data por um nada. Se a variável $TESTE estiver vazia é porque o padrão casou e o teste deu positivo.
Senão não é uma data válida.

A REGEX deve filtrar coisas como 99/13/-0005. Para coisas deste tipo o casamento falha e a variável $TESTE fica com alguma sujeira.

Qual a vantagem? Se shell usado não tiver suporte a REGEX usa-se o comando sed.
Mas também tem que funcionar essa atribuição de variável do $(...) senão tem que usar uns macetes de arquivos temporários

----------------------------------------------------------------------------------------------------------------
# apt-get purge systemd

Encryption works. Properly implemented strong crypto systems are one of the few things that you can rely on. Unfortunately, endpoint security is so terrifically weak that NSA can frequently find ways around it. — Edward Snowden



4. E as outras verificações?

Newton Teixeira
NewtonJr.

(usa CentOS)

Enviado em 26/12/2015 - 19:12h


Prezados, obrigado pelo help inicial. Com essas dicas já dá pra fazer a verificação se a data é válida ou não.

E para obter a data inicial e final ($data_ini e $data_fim) quando o usuário informa mês e ano em $mes e $ano? (SCRIPT 3)
Tipo se mês/ano for 01/2015 informado pelo usuário, $data_ini terá 01/01/2015 e $data_fim terá 31/01/2015.

E o teste para saber se o ano é bissexto?

E como fazer para obter o mês e ano a partir do sistema quando o usuário não informa os dois?

E como comparar duas datas ($data_ini e $data_fim) e não permitir que a $data_fim não seja menor do que a $data_ini?

Obrigado e no aguardo. PS: Tudo é feio em bash.


5. Re: Tratamento de parâmetros do tipo DATAS dd/mm/YYYY em shell scripts

Paulo Dias
di4s

(usa XUbuntu)

Enviado em 26/12/2015 - 21:41h

Oi, tudo bem?

A melhor forma de conseguir informações sobre esse assunto é usar os comandos:


man date
info date



Armazenar data do sistema:


data=$( date +%d/%m/%Y )


Armazenar o primeiro dia do mês atual:


data=$( date +01/%m/%Y )


Armazenar o ultimo dia do mês atual


data=$( date -d "$( date +%Y-%m-01 ) + 1 month - 1 day" +%d/%m/%Y )


Para verificar qual data é maior, recomendo transformar em segundos e comparar depois...


segundos=$( date -d $( date +%Y-%m-%d ) +%s )


E por último, para verificar se o ano é bissexto, verifique se a data %Y-02-29 é válida conforme explicou o Paulo ( não eu, o outro :)

Linux Professional Institute - LPIC-1
Novell Certified Linux Administrator - CLA

http://twitter.com/p4ulodi4s
http://www.prminformatica.com.br/


6. Re: Tratamento de parâmetros do tipo DATAS dd/mm/YYYY em shell scripts [RESOLVIDO]

Perfil removido
removido

(usa Nenhuma)

Enviado em 26/12/2015 - 23:17h

NewtonJr. escreveu:


Prezados, obrigado pelo help inicial. Com essas dicas já dá pra fazer a verificação se a data é válida ou não.

E para obter a data inicial e final ($data_ini e $data_fim) quando o usuário informa mês e ano em $mes e $ano? (SCRIPT 3)
Tipo se mês/ano for 01/2015 informado pelo usuário, $data_ini terá 01/01/2015 e $data_fim terá 31/01/2015.

E o teste para saber se o ano é bissexto?

E como fazer para obter o mês e ano a partir do sistema quando o usuário não informa os dois?

E como comparar duas datas ($data_ini e $data_fim) e não permitir que a $data_fim não seja menor do que a $data_ini?

Obrigado e no aguardo. PS: Tudo é feio em bash.


Anos bissextos:

1. Anos divisíveis por 4, exceto por item 2 e 3, fevereiro tem 29 dias. Ex. 2004, 2008 são. 2013 não;
2. Anos de século divisíveis por 400, fevereiro tem 29 dias. Ex: 1200, 1600, 2000 são. 1700, 1800, 1900 não;
3. Anos múltiplos de 3320 não são bissextos e fevereiro tem apenas 27 dias. Surpreendente, não?

Testes podem ser feitos com divisão inteira e com divisão por módulo (resto).

----------------------------------------------------------------------------------------------------------------
http://24.media.tumblr.com/tumblr_m62bwpSi291qdlh1io1_250.gif

# apt-get purge systemd

Encryption works. Properly implemented strong crypto systems are one of the few things that you can rely on. Unfortunately, endpoint security is so terrifically weak that NSA can frequently find ways around it. — Edward Snowden



7. Re: Tratamento de parâmetros do tipo DATAS dd/mm/YYYY em shell scripts [RESOLVIDO]

Marcelo Oliver
msoliver

(usa Debian)

Enviado em 27/12/2015 - 16:02h

E o teste para saber se o ano é bissexto?

Boa tarde, segue o SCRIPT para verificar MATEMATICAMENTE se o ano é bissexto, conforme as REGRAS que o listeiro_037 citou:

#!/bin/bash

TST01() {
echo -e "\n VERIFICA SE O ANO É BISSEXTO\n"
echo -n " INFORME O ANO"
read -p ": " -n4 ANO
if [ $((ANO%4)) -eq 0 ];then
[ $((ANO%100)) -ne 0 ] && { ZZ="É" ; MSG ; } || TST02
else
ZZ="NÃO É" ; MSG
fi
}

TST02() {
if [ $((ANO%400)) -eq 0 ];then
ZZ="É" ; MSG
else
ZZ="NÃO É" ; MSG
fi
}

MSG() {
echo -e "\n\n $ANO => $ZZ BISSEXTO <= \n" ; }
TST01


Calcula se é bissexto utilizando o comando date:
O ANO BISSEXTO tem 366 dias, logo:

read -p " ANO " -n4 -e ANO; [ $(date -d"$ANO-12-31" +%j) -eq 366 ] && echo -e " $ANO => É BISSEXTO" || echo -e " $ANO => NÃO É BISSEXTO" 


##############################################################################################

E como fazer para obter o MÊS e ANO a partir do sistema quando o usuário não informa os dois?

[ -z $MES ] && MES=$(date +"%m")
[ -z $ANO ] && ANO=$(date +"%Y")

##############################################################################################

E como comparar duas datas ($data_ini e $data_fim) e não permitir que a $data_fim não seja menor do que a $data_ini?

DATA no formato YYYY/MM/DD ou YYYY-MM-DD

(($(date -d"$data_ini" +%j) < $(date -d"$data_fim" +%j))) && echo -e "\n OKAY" || echo -e "\n NOKAY" 


É isso.

Att.:
Marcelo Oliver





8. Re: Tratamento de parâmetros do tipo DATAS dd/mm/YYYY em shell scripts

Paulo Dias
di4s

(usa XUbuntu)

Enviado em 27/12/2015 - 16:19h

msoliver escreveu:

E o teste para saber se o ano é bissexto?

Boa tarde, segue o SCRIPT para verificar MATEMATICAMENTE se o ano é bissexto, conforme as REGRAS que o listeiro_037 citou:

#!/bin/bash

TST01() {
echo -e "\n VERIFICA SE O ANO É BISSEXTO\n"
echo -n " INFORME O ANO"
read -p ": " -n4 ANO
if [ $((ANO%4)) -eq 0 ];then
[ $((ANO%100)) -ne 0 ] && { ZZ="É" ; MSG ; } || TST02
else
ZZ="NÃO É" ; MSG
fi
}

TST02() {
if [ $((ANO%400)) -eq 0 ];then
ZZ="É" ; MSG
else
ZZ="NÃO É" ; MSG
fi
}

MSG() {
echo -e "\n\n $ANO => $ZZ BISSEXTO <= \n" ; }
TST01


Calcula se é bissexto utilizando o comando date:
O ANO BISSEXTO tem 366 dias, logo:

read -p " ANO " -n4 -e ANO; [ $(date -d"$ANO-12-31" +%j) -eq 366 ] && echo -e " $ANO => É BISSEXTO" || echo -e " $ANO => NÃO É BISSEXTO" 


##############################################################################################

E como fazer para obter o MÊS e ANO a partir do sistema quando o usuário não informa os dois?

[ -z $MES ] && MES=$(date +"%m")
[ -z $ANO ] && ANO=$(date +"%Y")

##############################################################################################

E como comparar duas datas ($data_ini e $data_fim) e não permitir que a $data_fim não seja menor do que a $data_ini?

DATA no formato YYYY/MM/DD ou YYYY-MM-DD

(($(date -d"$data_ini" +%j) < $(date -d"$data_fim" +%j))) && echo -e "\n OKAY" || echo -e "\n NOKAY" 


É isso.

Att.:
Marcelo Oliver




Oi, tudo bem?

Não me entenda mal, mas acredito que a comparação entre datas está incorreta...

Veja:

Se data_ini='2014-10-01' e data_fim='2015-01-01', é retornado NOKAY.


Linux Professional Institute - LPIC-1
Novell Certified Linux Administrator - CLA

http://twitter.com/p4ulodi4s
http://www.prminformatica.com.br/


9. Re: Tratamento de parâmetros do tipo DATAS dd/mm/YYYY em shell scripts [RESOLVIDO]

Marcelo Oliver
msoliver

(usa Debian)

Enviado em 27/12/2015 - 22:01h

p4ulodi4s, boa noite.

Realmente, se o ANO de $data_ini for diferente do ANO de $data_fim, o resultado será incorreto, pois, estou utilizando o dia do ano para a comparação.
Uso esse método para datas do mesmo ano.
Para ANO diferente, usaria os segundos, como vc mesmo sugeriu, ou, a comparação direta das datas, tipo:
data_ini='2014-10-01'
data_fim='2015-01-01'
(($(sed 's/[[:punct:]]//g' <<< "$data_ini") < $(sed 's/[[:punct:]]//g' <<< "$data_fim")))&& echo "OK" ||echo "NOKAY"

Att.:
Marcelo Oliver




10. Re: Tratamento de parâmetros do tipo DATAS dd/mm/YYYY em shell scripts

Perfil removido
removido

(usa Nenhuma)

Enviado em 27/12/2015 - 23:07h

Para se comparar se uma data é maior, menor ou igual a outra pode ser feito o seguinte:

- Primeiro uma comparação de string. Se for igual declara-se igual. Senão, próximo passo;
- Compara-se os anos. Se um ano for maior ou menor, declara-se maior ou menor data. Senão próximo passo;
- Compara-se se os meses. Se um mês for maior ou menor, declara-se maior ou menor data. Senão próximo passo;
- Compara-se os dias. Se um dia for maior ou menor que outro, declara-se maior ou menor data. Aqui não pode dar igual por causa do primeiro teste.

Pode-se continuar estas etapas com horas, minutos e segundos.

Também as datas podem ser convertidas em um formato numérico que conta minutos ou segundos desde a década de 70 (é isto?) e realizar uma comparação direta com valores numéricos sem encher linguiça com essa série de comparações.

Pensei em outro método de ataque ao problema:

Tira-se o separador das datas escritas no formato ano/mês/dia e compara-se como números qual é maior ou menor.
Ex: 08/06/2004 fica 2004/06/08 que fica 20040608. É só comparar as datas neste formato como números.


----------------------------------------------------------------------------------------------------------------
http://24.media.tumblr.com/tumblr_m62bwpSi291qdlh1io1_250.gif

# apt-get purge systemd

Encryption works. Properly implemented strong crypto systems are one of the few things that you can rely on. Unfortunately, endpoint security is so terrifically weak that NSA can frequently find ways around it. — Edward Snowden



11. Re: Tratamento de parâmetros do tipo DATAS dd/mm/YYYY em shell scripts [RESOLVIDO]

Newton Teixeira
NewtonJr.

(usa CentOS)

Enviado em 29/12/2015 - 12:42h

p4ulodi4s escreveu:


segundos=$( date -d $( date +%Y-%m-%d ) +%s )




Paulo, Obrigado pelos códigos. Já estou usando eles.

Na parte de transformar pra segundos, fiz um teste com o seguinte código e está dando erro. Lembrando que as datas são informadas pelo usuário.

n_segundos_data_ini=$( date -d "$data_ini" +%s )
n_segundos_data_fim=$( date -d "$data_fim" +%s )

if [ $n_segundos_data_ini -gt $n_segundos_data_fim ]; then echo "Erro! Data final é menor do que a Data Inicial." && exit 1

Outra questão é que usuei o código que você informou da seguinte forma:

[ "$data_ini" ] || data_ini=$( date +01/%m/%Y )
[ "$data_fim" ] || data_fim=$( date -d "$( date +%Y-%m-01 ) + 1 month - 1 day" +%d/%m/%Y )

Mas a lógica certa no meu caso seria:

if "$data_ini" é vazio ou "$data_fim" é vazio then
data_ini=$( date +01/%m/%Y )
data_fim=$( date -d "$( date +%Y-%m-01 ) + 1 month - 1 day" +%d/%m/%Y )
fi

Como fazer isso em shell?

Por fim, eu usei o código do outro Paulo para testar se as datas são válidas.

if ! [[ "$data_ini" =~ ^(0[1-9]|[12][0-9]|3[01])/(0[1-9]|1[0-2])/([0-9]{4})$ ]]; then
echo "Data inicial inválida" >&2
exit 1
fi
year=${BASH_REMATCH[3]}
month=${BASH_REMATCH[2]}
day=${BASH_REMATCH[1]}
if ! date -d "$year/$month/$day" >/dev/null 2>&1; then
echo "Data inicial inválida" >&2
exit 1
fi

if ! [[ "$data_fim" =~ ^(0[1-9]|[12][0-9]|3[01])/(0[1-9]|1[0-2])/([0-9]{4})$ ]]; then
echo "Data final inválida" >&2
exit 1
fi
year=${BASH_REMATCH[3]}
month=${BASH_REMATCH[2]}
day=${BASH_REMATCH[1]}
if ! date -d "$year/$month/$day" >/dev/null 2>&1; then
echo "Data final inválida" >&2
exit 1
fi

Veja que repeti o mesmo código duas vezes. Como fazer isso usando uma função?

Obrigado pelos comentários. Está quase pronto. Oss!



12. Re: Tratamento de parâmetros do tipo DATAS dd/mm/YYYY em shell scripts

Paulo Dias
di4s

(usa XUbuntu)

Enviado em 29/12/2015 - 14:23h


Paulo, Obrigado pelos códigos. Já estou usando eles.

Na parte de transformar pra segundos, fiz um teste com o seguinte código e está dando erro. Lembrando que as datas são informadas pelo usuário.

n_segundos_data_ini=$( date -d "$data_ini" +%s )
n_segundos_data_fim=$( date -d "$data_fim" +%s )

if [ $n_segundos_data_ini -gt $n_segundos_data_fim ]; then echo "Erro! Data final é menor do que a Data Inicial." && exit 1


Qual erro?


Outra questão é que usuei o código que você informou da seguinte forma:

[ "$data_ini" ] || data_ini=$( date +01/%m/%Y )
[ "$data_fim" ] || data_fim=$( date -d "$( date +%Y-%m-01 ) + 1 month - 1 day" +%d/%m/%Y )

Mas a lógica certa no meu caso seria:

if "$data_ini" é vazio ou "$data_fim" é vazio then
data_ini=$( date +01/%m/%Y )
data_fim=$( date -d "$( date +%Y-%m-01 ) + 1 month - 1 day" +%d/%m/%Y )
fi

Como fazer isso em shell?


Dentro do comando test [] você pode usar -z para verificar strings vazias e -o como operador lógico OR. Além disso, é possível usar mais de um comando test por if. Assim sendo, é possível uma construção do tipo:

if [ -z "$data1" ] || [ -z "$data2" ] ; then
echo teste;
if



Por fim, eu usei o código do outro Paulo para testar se as datas são válidas.

if ! [[ "$data_ini" =~ ^(0[1-9]|[12][0-9]|3[01])/(0[1-9]|1[0-2])/([0-9]{4})$ ]]; then
echo "Data inicial inválida" >&2
exit 1
fi
year=${BASH_REMATCH[3]}
month=${BASH_REMATCH[2]}
day=${BASH_REMATCH[1]}
if ! date -d "$year/$month/$day" >/dev/null 2>&1; then
echo "Data inicial inválida" >&2
exit 1
fi

if ! [[ "$data_fim" =~ ^(0[1-9]|[12][0-9]|3[01])/(0[1-9]|1[0-2])/([0-9]{4})$ ]]; then
echo "Data final inválida" >&2
exit 1
fi
year=${BASH_REMATCH[3]}
month=${BASH_REMATCH[2]}
day=${BASH_REMATCH[1]}
if ! date -d "$year/$month/$day" >/dev/null 2>&1; then
echo "Data final inválida" >&2
exit 1
fi

Veja que repeti o mesmo código duas vezes. Como fazer isso usando uma função?

Obrigado pelos comentários. Está quase pronto. Oss!


No final do post dele, o Paulo, indica outra maneira melhor de fazer isso. Veja:


date -d "2015-02-01"
echo $?
date -d "2015-02-31"
echo $?


Se quiser usar da maneira que está, você pode inserir esse código em uma função.

Veja esse link sobre shell script no meu blog:

www.prmjuniorblog.blogspot.com.br/2013/01/iniciando-com-shell-script.html

Você vai encontrar alguns detalhes sobre as particularidades do bash e algumas construções uteis para terminar o seu script( Ex.: funções )


E também esse http://aurelio.net/shell/canivete/ . Nesse você vai encontrar exemplos de códigos.

Linux Professional Institute - LPIC-1
Novell Certified Linux Administrator - CLA

http://twitter.com/p4ulodi4s
http://www.prminformatica.com.br/



01 02



Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts