Calculando diferença entre duas datas no formato YYYYMMDDHH (Shell)

Neste post vou apresentar um script que escrevi há alguns meses atrás para calcular a diferença entre duas datas quaisquer, no formato YYYYMMDDHH (ou seja, no formato ANO, MÊS, DIA, HORA SINÓTICA).

Exemplo

Qual a diferença (em dias ou em horas) entre as datas “2013010100” e “2013011512”?

O script irá calcular a diferença entre estas duas datas e o resultado será dado em dias e em horas:

$ ./calc_date_diff.ksh 2013010100 2013011512

14 dia(s) e 12 hora(s) ou 348 hora(s)

Script

O script possui o seguinte algorítmo:

  1. Recebe as duas datas pela linha de comando;
  2. As datas são fornecidas como argumentos para a função datediff;
  3. Na função datediff, são separados os anos, os meses, os dias e os horários sinóticos;
  4. A partir disso, são formatadas as datas data1 e data2 no formato ‘YYYY-MM-DD HH:mm:ss UTC’;
  5. Verifica se a data2 é maior ou menor do que data1; se data2 for maior do que data1,
  6. Verifica se HH da data2 é igual a zero e se não é igual a HH da data1;
    1. Se esta verificação for verdadeira, então a diferença entre HH (horas) das datas Informadas será calculada por 24-HH da data1;
    2. Caso contrário, a diferença entre HH (horas) das datas será calculada pela diferença entre os HH da data2 e data1;
  7. Se HH da data1 for igual a zero e HH da data2 não for igual a HH da data1, então a diferença entre HH das datas é calculada por 24 menos HH da data2;
    1. Caso contrário, esta diferença será calculada pela diferença entre HH da data2 e data1.
#! /bin/ksh

#set -o xtrace

data_anl=${1}
data_fct=${2}

datediff(){

  yyyy_anl=$(echo ${data_anl} | cut -c 1-4)
  mm_anl=$(echo ${data_anl} | cut -c 5-6)
  dd_anl=$(echo ${data_anl} | cut -c 7-8)
  hh_anl=$(echo ${data_anl} | cut -c 9-10)

  yyyy_fct=$(echo ${data_fct} | cut -c 1-4)
  mm_fct=$(echo ${data_fct} | cut -c 5-6)
  dd_fct=$(echo ${data_fct} | cut -c 7-8)
  hh_fct=$(echo ${data_fct} | cut -c 9-10)

  data1=$(date -d "${yyyy_anl}-${mm_anl}-${dd_anl} ${hh_anl}:00:00 UTC" "+%s")
  data2=$(date -d "${yyyy_fct}-${mm_fct}-${dd_fct} ${hh_fct}:00:00 UTC" "+%s")

  if [ ${data2} -gt ${data1} ]
  then
    if [ ${hh_fct} -eq "0" -a ${hh_fct} -ne ${hh_anl} ]
    then
      hh_diff=$(( 24-${hh_anl} ))
    else
      hh_diff=$(( ${hh_fct}-${hh_anl} ))
    fi
  else
    if [ ${hh_anl} -eq "0" -a ${hh_fct} -ne ${hh_anl} ]
    then
      hh_diff=$(( 24-${hh_fct} ))
    else
      hh_diff=$(( ${hh_anl}-${hh_fct} ))
    fi
  fi

  dd_diff=$(((${data2}-${data1}) / 86400))

  hh_tot=$((${dd_diff}*24+${hh_diff}))

}

datediff ${data_anl} ${data_fct}

echo "${dd_diff} dia(s) e ${hh_diff} hora(s) ou ${hh_tot} hora(s)"

exit 0

É isso!

Anúncios

Teste de argumentos em Korn Shell

Quando se programa scripts no Shell, muitas vezes torna-se conveniente passar argumentos junto com o script para tornar sua execução mais dinâmica e genérica. Mas esta prática também pode trazer alguns problemas: o script pode ser executado mesmo com um argumento errado! Por isso, é importante testar os argumentos antes de se executar as funções ou comandos do script.

Neste artigo, criaremos um script Korn Shell (KSH) que contém algumas funções, que serão executadas dependendo do argumento passado pela linha de comando. As funções, por simplicidade, conterão apenas o comando “print” com uma mensagem indicando qual função foi chamada. Veja abaixo, as etapas e a lógica do teste de argumentos:

Estrutura do Script

O script que montaremos, terá a seguinte estrutura – que pode ser copiada para qualquer outro script que faça qualquer outra coisa e que receba argumentos de entrada:

#! /bin/ksh
# set -o xtrace
# Funcoes
funcao_01() {
  print "Voce escolheu a funcao_01"
}
funcao_02() {
  print "Voce escolheu a funcao_02"
}
funcao_03(){
  print "Voce escolheu a funcao_03"
}
ajuda(){
  print "Uso: ./teste.ksh <argumento>"
}
# Teste dos argumentos
if [[ $# = 0 ]]
then
  print "Erro: Nao foi passado nehum argumento!"
  ajuda
elif [[ #1 != 1 ]]
then
  print "Apenas um argumento e necessario!"
elif [[ ${1} = "funcao_01" ]]
then
  print "Executando funcao ${1}"
  funcao_02
elif [[ ${1} = "funcao_02" ]]
then
  print "Executando funcao ${1}"
  funcao_02
elif [[ ${1} = "funcao_03" ]]
then
  print "Executando funcao ${1}"
  funcao_03
else
  print "Comando desconhecido: ${1}"
  ajuda
  exit 1
fi
exit 0

O script acima pode ser dividido em duas partes: uma contendo as funções, e outra contendo o teste dos argumentos. Na primera parte há três funções definidas que são executadas apenas quando chamadas, ou melhor, quando invocadas pela linha de comando através do argumento passado. Um das funções, chama-se “ajuda” e pode ser utilizada quando nenhum argumento é passado junto com o script, quando um número errado de argumentos é passado junto com o script (por exemplo, quando é necessário apenas um argumento e dois ou mais argumentos são passados) ou quando um argumento inválido é passado junto com o script – neste caso não haveria uma função associada ao argumento passado. Na segunda parte, é feito o teste dos argumentos. Esta estrutura do script é vantajosa pois impede que comando perigosos (como um “rm -rf”) sejam executados na ausência de argumentos ou ainda na presença de um argumento errado. Geralmente, estas situações ocorrem com usuários que não têm o hábito de ler o script antes de executar.

Algorítimo dos testes

O algorítimo para o teste de argumentos é o seguinte:

  1. Teste se algum argumento é passado. Caso positivo, passa-se para o próximo teste; caso negativo, mostra-se a função “ajuda”.
  2. Teste se mais do que um argumento é passado. Caso negativo, passa-se para o próximo teste; caso positivo, mostra-se a função “ajuda”.
  3. Teste se o argumento passado está associado à função funcao_01. Caso positivo, executa-se a função funcao_01; caso negativo, passa-se para o próximo teste.
  4. Teste se o argumento passado está associado à função funcao_02. Caso positivo, executa-se a função funcao_02; caso negativo, passa-se para o próximo teste.
  5. Teste se o argumento passado está associado à função funcao_03. Caso positivo, executa-se a função funcao_03; caso negativo, encerra-se o script com status de erro (1).

Observe que os argumentos possíveis neste script têm o mesmo nome que as funções definidas: funcao_01, funcao_02 e funcao_03. Qualquer nome (ou argumento) passado junto com o script e que for diferentes destes nomes, fará com que nenhuma das funções definidas seja executada.

Conclusão

O teste de argumentos deve englobar todas as possibilidades. Neste simples caso, apenas um argumento é testado o que torna simples a estruturação dos testes. Aumentando-se o número de argumentos, a complexidade dos testes pode aumentar e é aí que o programador deve avaliar a necessidade de se utilizar muitos argumentos dentro de um script. A idéia principal é manter o script simples e enxuto.

Backup local simples com o rsync

O script abaixo tem por objetivo realizar o backup/sincronização (incremental) entre duas pastas diferentes, facilitando e agilizando p processo de cópia de grandes volumes de arquivos.

Algorítmo

O algorítmo deste script segue os seguintes passos:

  1. Verificação dos argumentos de entrada:
    1. Verifica se a quantidade de argumentos não é dois (caso postivo, mostra a função ajuda e finaliza);
    2. Verifica se a quantidade de argumentos é dois (caso positivo, verifica os locais – se são acessíveis ou não);
  2. Com os dois argumentos de entrada (origem e destino, respectivamente), chama a função backup
  3. Finaliza o script
#!/bin/ksh
#set -o xtrace
ajuda() {
print "Uso: ./backup_rsync.ksh <origem> <destino>"
 exit 1
}
backup() {
origem=${1}
 destino=${2}
rsync -av ${origem}/ ${destino}/
}
if [ $# -ne 2 ]
 then
ajuda
elif [ $# -eq 2 ]
 then
print "Verificando os locais:"
 print "${1}"
 print "${2}"
if [ ${1} == ${2} ]
 then
print "${1} e o mesmo local de destino (${2})"
 exit 1
elif [ ${1} != ${2} -a -r ${1} -a -w ${2} ]
 then
backup ${1} ${2}
else
print "${1} inacessivel ou ${2} nao pode ser escrito"
 exit 1
fi
else
ajuda
fi
exit 0

Exemplo

No exemplo abaixo, serão sincronizadas as pastas “teste1” e “teste2”, ambas presentes no mesmo diretório em que o script será executado (o que não é uma necessidade). Cada uma das pastas contém os arquivos “arquivo1” e “arquivo2”, respectivamente. O objetivo é fazer com que a “pasta2” contenha os “arquivo1” e “arquivo2”. Em qualquer caso, nenhum arquivos é apagado.

./backup_rsync.ksh teste2 teste1
 Verificando os locais:
 teste2
 teste1
 sending incremental file list
 arquivo2
sent 127 bytes received 31 bytes 316.00 bytes/sec
 total size is 0 speedup is 0.00

Observações

  • Na primeira vez em que o script for utilizado, pode ser que leve um tempo maior; depois disso, o processo de sincronização é mais rápido;
  • Na linha 17 do script, a barra no final de ${origem} e ${destino} é necessária para que o conteúdo de ${origem} seja armazenado no primeiro nível de ${destino}, caso contrário a pasta ${origem} será criada dentro de ${destino}.

Minha lista de comandos úteis no shell do linux

Depois de algum tempo fazendo scripts e tentando lançar uma solução aqui e ali, montei uma lista de alguns comando do shell do linux que me quebram uma galhão. Espero que lhe ajude também! Só para melhorar a diagramação dos comando, coloquei alguns “\n” para quebrar a linha, mas você suprimir esta parte e colocar tudo em uma linha só:

1. Operações de busca com o comando find

$ find <diretorio> -name ""

Exemplos:

1.1 Procurar arquivos com o nome “run” na pasta atual em diante

 $ find . -name "run" 

1.2 Procurar arquivo com a extensão “.ksh” na pasta /etc

 $ find /etc -name "*.ksh" 

1.3 Procurar por links simbólicos na pasta atual em diante

 $ find . type -l 

1.4 Procurar por links simbólicos na pasta atual em diante, com saída formatada (indicando a origem e o destino do link)

 $ find . -type l -printf "%p -> %l\n" 

1.5 Apagar todos os links simbólicos encontrados utilizando o comando xargs

 $ find . -type l | xargs rm -rf 

1.6 Procurar todos os arquivos de nome “monitor.t” em pastas com nome sequencial formatado de “001” a “040”, na pasta atual

 $ for pasta in $(seq -f "%03g" 1 40); do echo $pasta; cd $pasta; \n 
 if test ! -e monitor.t; then echo ">>> $pasta falhou"; fi; cd -; done 

2. Operações de pesquisa em arquivos com o comando grep

$ grep <opcoes> <expressao> <arquivo(s)>

Exemplos:

2.1 Pesquisa recursiva pela expressão “letkf” em todos os scripts ksh na pasta atual em diante

 $ grep -R "letkf" *.ksh 

2.2 Pesquisa recursiva pela exporessão “letkf” ou “LETKF” (ou uma mistura de maiúsculas/minúsculas) em todos os scripts ksh na pasta atual em diante

 $ grep -iR "letkf" *.ksh 

3. Operações com arquivos utilizando o comando awk

 $ awk <opcoes> <comandos> <arquivo>

ou

 $ cat <arquivo> | awk <opcoes> <comandos>

Exemplos:

3.1 Filtrando um arquivo com colunas separadas por espaços, mostrando apenas a coluna “n”

 $ cat <arquivo> | awk -F " " '{print $n}'

4. Operações com arquivos utilizando o comando sed

 $ sed <opcoes> <comandos> <arquivo(s)>

ou

 $ cat <arqvuivo> | sed <opcoes> <comandos>

Exemplos:

4.1 Substituindo todas as ocorrências da string “A” pela string “B” em todos os scripts .ksh (apenas na pasta atual)

 $ sed -i "s,A,B,g" *.ksh 

4.2 Comentando a linha “n” em um arquivo (comentário é dados por #)

 $ sed -i 'ns/^/#/' 

4.3 Inserindo um comentário na primeira linha de um arquivo

 $ sed -i "1s/^/# Comentario/" > 

5. Criando uma sequência de números no shell com o comando seq

 $(seq <inicio> <fim>)

Exemplos:

5.1 Imprimir no shell a sequencia de 1 a 20

 $ echo $(seq 1 20) 

6. Operações com loops no shell:

Comando for; do; done

Exemplos:

6.1 Imprimir a sequência de 1 a 30 utilizando o comando de laço for

 $ for i in $(seq 1 30); do echo $i; done 

6.2 Renomear todos os arquivos com extensão .txt adicionando uma nova extensão .dat

 $ for i in `find . -name *.txt`; do mv ${i} ${i}.dat; done 

6.3 Alterar todos os arquivos com a extensão .TQ0213L042 para .T213L42, utilizando o for e o sed

 $ for arq1 in `ls *.TQ0213L042`; do arq2=`echo ${arq1} | sed "s,TQ0213L042,T213L42,g"`; \n 
 cp ${arq1} ${arq2}; done 

Comando until; do; done

Exemplos:

6.4 Aguardar até que um arquivo esteja disponível

 $ until [ -e <arquivo> ]; do sleep .1s; done

Comando if; then; fi

Exemplos:

6.5 Verifica se uma pasta existe; se não existir, cria, se existir sai do teste

 $ if test ! -s <pasta>; then mkdir -p <pasta>; fi

6.6 Lê um lista chamada “lista” com os nomes das pastas que contém um script de submissão, dá permissão de execução e executa

 $ for i in `cat lista`; do cd $i; chmod +x qsub_interface.g2s.$i; \n 
 ./qsub_interface.g2s.$i; cd -; done

Comando while; do; done

6.6 Faz um loop em cima de datas; verifica se a primeira data é menor ou igual do que a segunda. Quando esta condição é alcançada, sai do loop

 $ while [ <data_inicio> -le <data_fim> ]; do <operacoes>; done

Referências Úteis: