Decidi criar fn para um loop while de validação que reutilizarei em todo o meu código:
loopFN() {
while true; do
if [ "$1" == "null" ] || ([ "$2" == "phone number" ] && ! [[ "$1" =~ $phone_regex ]]); then
printf 'enter a valid %s\n' "$2"
read
$1=$REPLY
else
break
fi
done
}
Estou chamando assim:
local number="null"
loopFN "$number" "phone number"
e minha expressão regex para o número de telefone é:
phone_regex="^[0-9]{3}-[0-9]{3}-[0-9]{4}"
o erro que estou recebendo é:
./newmenu.sh: line 27: null=dasd: command not found
onde "dasd" é minha entrada quando solicitado
é uma má prática reutilizar um loop while? devo apenas reescrevê-lo quantas vezes? ou minha sintaxe está errada em algum lugar?
o problema está por aqui, eu acho (a linha 27 que o erro está referenciando):
$1=$REPLY
onde estou tentando definir a variável que foi passada para o fn, que neste caso seria $number, meu pensamento inicial foi que não estava funcionando porque o var era local, mas removi a palavra-chave local e o erro persiste
EDITAR:
Eu adicionei algumas alterações conforme conselho:
loopFN() {
while true; do
if [[ $1 = null ]]; then
printf>&2 'enter a valid %s\n' "$2"
IFS= read -r "$1"
elif [[ $1 = null || ( $2 = 'phone number' && ! $1 =~ $phone_regex ) ]]; then
printf>&2 "enter a valid phone number\n"
IFS= read -r "$1"
else
break
fi
done
}
$1=$REPLY
não é entendido como uma atribuição de variável porque o que resta de=
($1
) não é um nome de variável válido, então é apenas tratado como um argumento de comando ou lista de argumentos de comando (como$1
e$REPLY
sem aspas, estão sujeitos a split + glob) o primeiro de que é tratado como o nome do comando.Você precisaria de:
Pedir ao shell para avaliar
contents_of_$1=$REPLY
como código shell, e assumindo que o conteúdo de$1
é um nome de variável válido, isso atribuiria o valor de$REPLY
à variável correspondente (e se$1
fossereboot;foo
, isso seria reinicializado obviamente).Aqui você também pode fazer:
Que chamaria
read
com o conteúdo de$1
como argumento, eread
não se importa e não teria como saber se o nome daquela variável foi passado literalmente ou o resultado de uma expansão.Essa ainda é uma vulnerabilidade de injeção de comando (como quando
$1
isfoo[`reboot`]
).De qualquer forma, a sintaxe para ler uma linha é
IFS= read -r line
, não.read line
Observe também que
(...)
, fora da[[...]]
construção no estilo Korn, é necessário iniciar um subshell, seuDeveria ser:
Porém , se
$1
for para ser um nome de variável, isso$1 =~ $phone_regex
faz pouco sentido. Os números de telefone raramente são nomes de variáveis válidos.Se você quisesse verificar o conteúdo da variável cujo nome está em
$1
, então você usaria${!1}
qual é a sintaxe do bash para desreferenciar uma variável ou usaria referências de nome no estilo ksh93 com versões mais recentes do bash, conforme sugerido pelo ChatGPT.Observe que, em qualquer caso, é o nome da variável (
number
) e não o valor ($number
) que você precisa passar para sua função.Os prompts e interações do usuário geralmente vão para stderr, sendo stdout reservado para a saída real que seu comando produz e que outra coisa pode querer reutilizar/pós-processar.
Especificamente com o
bash
shell, você pode usar a-p
opção internaread
para emitir op
rompt (que também será enviado para o stderr):Enquanto na maioria dos outros shells do tipo Korn a sintaxe é:
Se a função pretende inserir um número de telefone, perguntando repetidamente até que o usuário insira um número válido e retorne-o na variável cujo nome é fornecido no primeiro argumento, então com sintaxe específica do bash, seria parecido com:
(usar
[0123456789]
em vez de[0-9]
as[0-9]
pode corresponder a qualquer coisa e ancorar a regex no final e no início).Para uma função genérica
input
que usa o tipo de entrada e o regexp de validação como argumento:Observe o
_
prefixo nas variáveis locais, para que o usuário possa fazerinput type type '^(good|bad)$'
isso, por exemplo, sem que uma$type
variável local entre em conflito com a$type
variável do usuário que deseja retornar.