Para cigs.sh - o script completo pode ser encontrado aqui - escrevi a seguinte lógica feia (mas funcionando perfeitamente) para imprimir e formatar a saída do script, em parte apenas para descobrir todos os casos extremos, mas também porque não vi nenhum outra alternativa.
...
if [[ $W -le 0 && $D -le 0 && $H -eq 1 ]]; then string="$H hour"
elif [[ $W -le 0 && $D -le 0 && $H -gt 1 ]]; then string="$H hours"
elif [[ $W -le 0 && $D -eq 1 && $H -le 0 ]]; then string="$D day"
elif [[ $W -le 0 && $D -eq 1 && $H -eq 1 ]]; then string="$D day and $H hour"
elif [[ $W -le 0 && $D -eq 1 && $H -gt 1 ]]; then string="$D day and $H hours"
elif [[ $W -le 0 && $D -gt 1 && $H -le 0 ]]; then string="$D days"
elif [[ $W -le 0 && $D -gt 1 && $H -eq 1 ]]; then string="$D days and $H hour"
elif [[ $W -le 0 && $D -gt 1 && $H -gt 1 ]]; then string="$D days and $H hours"
elif [[ $W -eq 1 && $D -le 0 && $H -le 0 ]]; then string="$W week"
elif [[ $W -eq 1 && $D -le 0 && $H -eq 1 ]]; then string="$W week and $H hour"
elif [[ $W -eq 1 && $D -le 0 && $H -gt 1 ]]; then string="$W week and $H hours"
elif [[ $W -eq 1 && $D -eq 1 && $H -le 0 ]]; then string="$W week and $D day"
elif [[ $W -eq 1 && $D -gt 1 && $H -le 0 ]]; then string="$W week and $D days"
elif [[ $W -eq 1 && $D -eq 1 && $H -eq 1 ]]; then string="$W week, $D day and $H hour"
elif [[ $W -eq 1 && $D -eq 1 && $H -gt 1 ]]; then string="$W week, $D day and $H hours"
elif [[ $W -eq 1 && $D -gt 1 && $H -eq 1 ]]; then string="$W week, $D days and $H hour"
elif [[ $W -eq 1 && $D -gt 1 && $H -gt 1 ]]; then string="$W week, $D days and $H hours"
elif [[ $W -gt 1 && $D -le 0 && $H -le 0 ]]; then string="$W weeks"
elif [[ $W -gt 1 && $D -le 0 && $H -eq 1 ]]; then string="$W weeks and $H hour"
elif [[ $W -gt 1 && $D -le 0 && $H -gt 1 ]]; then string="$W weeks and $H hours"
elif [[ $W -gt 1 && $D -eq 1 && $H -le 0 ]]; then string="$W weeks and $D day"
elif [[ $W -gt 1 && $D -gt 1 && $H -le 0 ]]; then string="$W weeks and $D days"
elif [[ $W -gt 1 && $D -eq 1 && $H -eq 1 ]]; then string="$W weeks, $D day and $H hour"
elif [[ $W -gt 1 && $D -eq 1 && $H -gt 1 ]]; then string="$W weeks, $D day and $H hours"
elif [[ $W -gt 1 && $D -gt 1 && $H -eq 1 ]]; then string="$W weeks, $D days and $H hour"
elif [[ $W -gt 1 && $D -gt 1 && $H -gt 1 ]]; then string="$W weeks, $D days and $H hours"
fi
colour1='\033[0;31m'
colour2='\033[0;32m'
if (($elapsed < threshold))
then echo -e "${colour1}It's been $string since you last bought a $item."
else
echo -e "${colour2}It's been $string since you last bought a $item."
fi
Talvez eu esteja apenas sendo burro, mas por mais embaraçoso que seja o código acima, não consigo ver uma maneira melhor de reescrevê-lo. Existe um, e em caso afirmativo, o que é?
Uma abordagem alternativa é começar com a versão longa e detalhada e reduzi-la usando padrões correspondentes para remover plurais, etc. Eu não sei se é mais radável, então pode ser mais difícil de manter.
Isso usa a sintaxe de expansão de parâmetro do bash, onde
${parameter/pattern/replacement}
tenta corresponder ao padrão glob e, se encontrado, o substitui. Isso é usado para "corrigir" os "erros" como "1 semana" para torná-lo "1 semana". Para evitar também a correspondência21 weeks
, a string inicial tem um espaço extra no início, para que o espaço possa estar no padrão para garantir que apenas "1" seja correspondido.A sintaxe
${parameter#pattern}
exclui a correspondência se encontrada no início do parâmetro. Isso é usado para excluir "0 semanas".A sintaxe
${parameter%pattern}
exclui a correspondência se encontrada no final do parâmetro. Isso é usado para excluir "e 0 horas". As outras correções excluem um espaço se for deixado no início ou no final, ou uma vírgula se for deixado no final, ou um "e" e espaço se for deixado no início.A linha final substitui a vírgula por " e" se não houver "e" na string. Isso corresponde a, digamos, mudar "2 semanas, 3 dias" para "2 semanas e 3 dias", onde já removemos o "e 0 horas".
Se você estiver executando o script no bash, que tal isso (com base no comentário de Jeff Zeitlin):
Aviso: isso praticamente requer bash. zsh também tem arrays, mas numera as entradas de forma diferente (começando em 1 em vez de 0), então a seção final falharia estranhamente em zsh.
Nos dias anteriores ao bash ter
[[ ]]
expressões, as instruções case eram muito usadas para correspondência de padrões. A lista de if's pode ser convertida em uma instrução de caso único (o+
sinal é apenas um separador arbitrário):Isso ainda é difícil de digerir, mas se separarmos as palavras no plural com variáveis contendo an
s
ou não, acase
declaração é muito mais simples:Bash suporta funções , então talvez tente uma função. Esbocei (em pseudocódigo) uma possível tática a ser tomada.
O "s" é fácil de manusear porque só deve ser singular se o valor for um. A vírgula e o "e" são decisões baseadas em contagem, portanto, uma função retorna uma string vazia ou "apropriadamente pluralizada", e a próxima função é usada para incrementar um contador com base na natureza vazia da string.
O contador é então usado para manipular duas strings separadoras e, em seguida, a coisa toda é montada às cegas.
Pseudo-código: