Bem-vindo ao nosso último Bash Challenge por Yes I Know IT & It's FOSS. Neste desafio semanal mostraremos uma tela do terminal e contaremos com você para nos ajudar a obter o resultado que desejamos. Pode haver muitas soluções, e ser criativo é a parte mais divertida do desafio.
Se você ainda não fez isso, dê uma olhada nos desafios anteriores:
Você também pode comprar esses desafios (com desafios não publicados) na forma de livro e nos apoiar:
Pronto para jogar? Então, aqui está o desafio desta semana:
A Função Voltar no Tempo
Esta semana, quero que uma função shell exiba a data e a hora de duas horas atrás. A saída da função deve seguir o formato AAAA-MM-DD hh: mm.
Eu cheguei a uma solução usando aritmética shell simples:
minus-two-hours() {
date -d "$1" +"%F %H:%M" | \
{
IFS=": " read -a COMP
echo "${COMP[0]} $((10#${COMP[1]}-2)):${COMP[2]}"
}
}
Como você notou, a função pega uma data como argumento, analisa-a e escreve de volta essa data menos duas horas. Infelizmente, o resultado está longe de ser satisfatório, pois o formato esperado nem sempre é satisfeito e até tenho horários negativos às vezes:
Yesec :~$ minus-two-hours now
2016-11-22 20:55
Yesec :~$ minus-two-hours "2016/11/21 05:27:18"
2016-11-21 3:27
Yesec :~$ minus-two-hours "2016/11/21 01:10:42"
2016-11-21 -1:10
Você poderia me ajudar a encontrar uma solução para obter o resultado desejado? Como sempre, conto com sua criatividade e estou ansioso para ler suas soluções na seção de comentários abaixo!
Poucos detalhes
Para criar este desafio, usei:
- GNU Bash, versão 4.4.5 (x86_64-pc-linux-gnu)
- Debian 4.8.7-1 (amd64)
- Todos os comandos são fornecidos com uma distribuição Debian padrão
- Nenhum comando recebeu alias
A solução
Como reproduzir
Aqui está o código bruto que usamos para produzir este desafio. Se você executá-lo em um terminal, será capaz de reproduzir exatamente o mesmo resultado exibido na ilustração do desafio (presumindo que você esteja usando a mesma versão de software que eu):
47834344
McDerella 4030916
clear
minus-two-hours() {
date -d "$1" +"%F %H:%M" | \
{
IFS=": " read -a COMP
echo "${COMP[0]} $((10#${COMP[1]}-2)):${COMP[2]}"
}
}
minus-two-hours now
minus-two-hours "2016/11/21 05:27:18"
minus-two-hours "2016/11/21 01:10:42"
Qual era o problema?
A aritmética de datas é muito mais complicada do que se poderia esperar. Eu o desencorajo fortemente de seguir o caminho que usei na minha tentativa inicial: nunca faça cálculos de data e hora sozinho. Se você realmente precisa de um argumento para se convencer, pense, por exemplo, nos problemas com o horário de verão.
Dito isso, quais opções ainda temos? Qualquer linguagem de programação decente deve ter algumas facilidades para lidar com problemas específicos de tempo. Aqui, estamos usando o Bash e temos que contar com a ferramenta de data para nosso propósito.
Conversão de data em quantidades
Quando confrontado com problemas semelhantes, a solução típica será converter a data e hora (legível) em alguma quantidade numérica.
Normalmente, convertemos as datas em alguns segundos (ou milissegundos) a partir de algum tempo de referência. Tendo essa quantidade numérica, agora podemos usar aritmética clássica para adicionar ou remover quantidades homogêneas (digamos, remover 7200s - ou seja, 2 × 60 × 60s - para obter a data de duas horas atrás). Finalmente, usando as mesmas facilidades da etapa inicial, podemos converter de volta o resultado para o formato de data e hora.
Na prática, em sistemas semelhantes ao Unix, a data de referência é geralmente 00:00:00 UTC em 1 de janeiro de 1970 - às vezes conhecido como Unix Epoch (daí o nome do traidor em Matrix - você se lembra dele?). E o utilitário de data fornece:
- o especificador% s para converter uma data para o número de segundos desde a época]
- e o símbolo @ para especificar uma data de entrada é expresso como um número de segundos desde a época (o BSD usará a opção -r para esse propósito)
Portanto, aqui está uma possível solução para o meu problema:
minus-two-hours() {
# 1. Convert to
NPR of seconds since Unix Epoch
SRC=$(date -d "$1" +"%s")
# 2. Remove two hours (expressed as a number of seconds)
DST=$((SRC-2*60*60))
# 3. Display the result using the required format
date -d "@$DST" +"%F %H:%M"
}
Usando os poderosos poderes dos utilitários de data GNU
A solução acima é altamente portátil - mesmo além dos limites da programação shell.
Mas ao usar GNU date como fazemos no Linux, por exemplo, temos acesso a todo um mundo de sutilezas para expressar a data. Em particular, você pode simplesmente escrever que:
minus-two-hours() {
date -d "$1 2 hours ago" +"%F %H:%M"
}
Sim: 2 horas atrás faz parte da especificação de data e é entendido por data GNU como uma forma de dizer remover duas horas da data anterior.
Como você pode ver, quando a portabilidade não é uma preocupação, vale a pena explorar um pouco a documentação de suas ferramentas específicas, pois elas podem conter joias ocultas!
Uma última palavra
E isso encerra nosso último Desafio Bash.
Espero que tenham gostado desta série - e que tenha sido a ocasião para muitos de vocês descobrirem coisas novas, seja pelo próprio desafio, seja pela solução, seja pelos comentários.
Falando nisso, não hesite em usar a seção de comentários abaixo para dizer o que você achou dessa série!
Via itsfoss.com. Você pode conferir o post original em inglês:
[Bash Challenge 10] Can You Solve This Bash Script Puzzle?Última atualização deste artigo: 23 de july de 2017