Eu gostaria de ter um script bash que me permitisse chamar uma função para ver se alguns dados estão contidos em um arquivo e, se não falhar, o script principal, como o seguinte, que é simplificado para manter isso no ponto.
Isso não funciona (não sai do script principal quando o sub shell falha).
Como posso escrever a require_line
função para que eu pudesse dizer mais de 20 deles em um arquivo como esse
VALUE1=$(require_line "myKey1")
VALUE2=$(require_line "myKey2")
...
e não exigir um se em torno de cada um?
#!/bin/bash
set -eo pipefail
VALUE=$(require_line "myKey")
require_line(){
local KEY=$1
local DATA=$(cat /tmp/myfile)
local INFO=$(echo "$DATA" | grep "$KEY")
if [ ! -z "$INFO" ]
then
echo "Key not found in $DATA, key: $KEY"
exit 1;
fi
echo "$INFO"
}
Vamos reestruturar
require_line
um pouco seu script para ajudá-lo.Primeiro, podemos nos livrar do inútil
cat | grep
. Em segundo lugar, podemos usargrep
o comportamento intrínseco de 's para indicar o sucesso ou fracasso da busca porKEY
, bem como imprimir a chave, se encontrada, parastdout
.Isso aproveita o comportamento interno do
grep
. Se a chave for encontrada,grep
imprime a linha correspondente e retorna sucesso. Caso contrário, aelse
ramificação é tomada e uma mensagem é impressa indicando que a chave não foi encontrada. Além disso, no casogrep
de falha, a mensagem de erro é impressa parastderr
que a mensagem de erro não seja confundida com uma correspondência válida encontrada em$FILE
.Você pode modificar ainda mais
require_line
para aceitar um nome de arquivo como$2
parâmetro alterando a linha para lerlocal FILE="$2"
e, em seguida, passando o nome de arquivo desejado cada vez que invocarrequire_line
.Agora, com isso no lugar....
Você realmente precisa armazenar cada VALUEn para KEYn ou só precisa garantir que todos estejam presentes?
Agora que você tem uma
require_line
função que retorna claramente um valor de sucesso ou falha, você pode simplesmenteAND
juntar todos os seus testes. Assim que qualquer um deles falhar, o teste geral falhará.Supondo que você precise dos valores de correspondência reais, a maneira prolixa de fazer isso seria:
Isso será tedioso se você tiver várias chaves para verificar. Usar uma matriz pode ser melhor:
Executando esse código com alguns dados de exemplo:
Por fim, se você realmente só precisa verificar se todas as chaves estão presentes sem precisar saber quais são os valores de correspondência, o código orientado a matriz acima pode ser simplificado para simplesmente fazer um loop até que todas as chaves sejam encontradas ou abortar na primeira chave que está faltando.
Sair do subshell não sairá do script principal como você o experimentou.
Penso em 3 (e meia) soluções:
set -e
para que qualquer comando com falha ("não testado") (ou subshell) saia imediatamente do script principal (isso pode ser um exagero ou causar outros problemas),trap
|| exit $?
depois de cadaVALUEn=$(...)
assimVALUE=$(require_line "myKey") || exit $?
,eval
.Esse terceiro não exatamente "requer um if em torno de cada um" e ainda seria uma sintaxe bastante compacta IMHO.
Aliás , essa linha
...é realmente inútil se você sair de todo o script logo após isso porque a frase será armazenada na
$VALUEn
variável que não será exibida.Sugiro imprimir
stderr
assim:Exemplos
Exemplo para a solução 1
Mas se eu remover
set -e
desde o início, recebo:Exemplo para a solução 2
Exemplo para a solução 3
Exemplo para solução 4