[Bash Challenge 10] Você consegue resolver este quebra-cabeça do Bash Script?

Teste seus conhecimentos de script Bash resolvendo este pequeno quebra-cabeça Bash.

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

Quebra-cabeça de script Bash 10

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