Por que receberei uma expressão inteira esperada?

4

Então, estou tentando criar um sistema de menu no bash como uma experiência de aprendizado. Tenho certeza que existem inúmeras maneiras, e até mesmo "melhores" maneiras de fazer isso, mas o que eu tenho é algo assim ...

echo "
2nd Menu
********
1) command
2) command
M) Main menu
X) Exit program
"
read -p "Choose option >" opt
if [ "$opt" -eq 1 ]; then
    commands
elif [ "$opt" -eq 2 ]; then
    commands
elif [[ "$opt" = [m,M] ]]; then
    main #calls the function main()
elif [[ "$opt" = [x,X] ]]; then
    exit
else
    echo "Invalid option"
    main
fi

O script funciona para todas as opções, exceto o "X" Sair do programa ". Quando eu executo "X" ou "x", recebo este erro ...

./acct-mgr.sh: line 10: [: x: integer expression expected
./acct-mgr.sh: line 12: [: x: integer expression expected
Invalid option

Isso está me desconcertando! Tenho certeza que estou usando o operador de comparação correta para cada tipo de dados (-eq para inteiros e = para strings), juntamente com o fato de que a opção EVERY funciona EXCETO que "x".

Qualquer ajuda é muito apreciada. Obrigado.

P.S. - Formas alternativas de alcançar o resultado desejado são muito apreciadas, mas mesmo assim eu gostaria de saber por que isso não está funcionando para a minha edificação. Obrigado novamente.

    
por dlowrie290 07.10.2017 / 04:51

2 respostas

10

Quando você insere M / m ou X / x, está comparando um não-número a um número usando -eq e isso está causando a falha. Se você quiser que $opt seja uma string, trate-a como uma string o tempo todo:

...
if [ "$opt" = "1" ]; then
    commands
elif [ "$opt" = "2" ]; then
    commands
...
    
por 07.10.2017 / 05:06
6

Como complemento ao que @Andy já disse , observe que

[[ "$opt" = [m,M] ]]

corresponde a m , , e M . Se você quisesse corresponder apenas em m e M , isso seria

[[ "$opt" = [mM] ]]

Aqui, usaria uma construção padrão case :

case $opt in
  (1)   commands;;
  (2)   commands;;
  (m|M) main;;
  (x|X) exit;;
  (*)   echo >&2 "Invalid option"; main
esac

Observe também que o comportamento de read depende do valor atual de $IFS . Se você quisesse ler uma linha do usuário, usaria:

IFS= read -r opt

Para ler um caractere:

IFS= read -rn1 opt

Para ler uma palavra (leia uma linha, mas ignore o espaço e as abas iniciais e qualquer outra coisa além da primeira palavra):

IFS=$' \t' read -r opt ignored

Ou para permitir que o uso inclua espaços ou guias na palavra prefixando com barra invertida:

IFS=$' \t' read opt ignored
    
por 07.10.2017 / 08:43