como ya lo expliqué en mi otro hiloDeclaración while: No se puede lograr que funcione el compuesto de múltiples condicionesEstoy intentando analizar los argumentos de entrada de alguna recursive descent
manera.
Lamentablemente, aunque los participantes tenían mucho más conocimiento que yo y dieron buenos consejos, el problema persiste.
También hurgué un poco más en la consola sin éxito.
Tengo las siguientes dos while
declaraciones en mi script, que son problemáticas, porque obviamente la endOfInput
función no tiene influencia para finalizar el ciclo while.
Hurgaba y a veces ni siquiera entraba en el bucle.
Entonces el exterior while-statement
es el siguiente:
while [ $current_token_index -le $ARGC ] && [[ "$current_token" =~ ^[a-zA-Z0-9_-]+$ ]] || ! [[ "$current_token" =~ ^[0-9]{1,2}(\.[0-9]{,2})*$ ]]; do
Además, nuevamente, aquí está el interior.while-statement
while [[ "$current_token_index" -le "$ARGC" ]] && [[ "$current_token" =~ ^[0-9]{1,2}(\.[0-9]{,2})*$ ]]; do
También los métodos auxiliares:
isWord? () {
local pattern="^[a-zA-Z0-9_-]+$"
if [[ $1 =~ $pattern ]]; then
echo true
else
echo false
fi
}
isVersionNumber? () {
local pattern="^[0-9]{1,2}(\.[0-9]{,2})*$"
if [[ $1 =~ $pattern ]]; then
echo true
else
echo false
fi
}
EO_ARGS=false
function endOfInput? {
if [ $current_token_index -ge $ARGC ]; then
EO_ARGS=true
fi
echo $EO_ARGS
}
Además, la eat!
función que nos proporciona el siguiente token de una copia del posicional
eat! () {
current_token=${ARGV[$current_token_index]}
((current_token_index += 1))
current_char=${current_token:0:1}
}
Y encima de eso declaro
# as set by eat!()
current_token=""
current_char=""
current_token_index=0
curent_char_index=0
Mi pregunta se refiere a la posibilidad de que endOfInput
(originalmente: endOfInput?
pero lo eliminé, ?
como aprendí, en bash tiene un significado comodín que puede generar problemas. En Ruby puedes elegir la mayoría de los caracteres especiales como identificadores sin problemas. Obviamente, hay muchas advertencias en bash si viene de otros lenguajes de programación) ... de modo que el valor de verdad de la endOfInput
función no se evalúa correctamente a través de múltiples definiciones de la función junto con diferentes pruebas o comparando sintaxis en una declaración while con sus propias exigencias para la evaluación. si una condición se cumple o no. Entonces hay tres cosas en la ecuación que juntas son responsables de la formulación de la constelación condicional.
El problema con los while
cabezales de bucle publicados anteriormente es que no se ingresan o no se detienen. Dependiendo de cómo pruebo endOfInput
o cómo lo agrupo.
Si reemplazo ! endOfInput && ...
solo por, ! false && ...
por ejemplo, se ingresará el bucle while; en el otro caso, no.
Por eso conjetura que debe haber un problema con la función. No se evalúa correctamente. El problema puede ser 1) la definición de endOfInput
, o 2) cómo se prueba, es decir,
2.1 conque Examen(cosas como
-eq
, comparación de cadenas como=
, operadores aritméticos como==
)2.2quéestá probado. Es decir
- 2.2.1 Una cadena "verdadero"/"falso"
- 2.2.2
true
yfalse
como literales - 2.2.3
0
portrue
y1
parafalse
3 ¿Cómo se devuelve correctamente este valor?
- 3.1 por
return
- 3.2 por
exit
- 3.3 por
echo
- 3.1 por
4 ¿mediante qué construcción de prueba se compara el valor devuelto con otra cosa?
- 4.1 ninguno, solo ejecutando la función
- 4.2 corchetes (
[
o[[
comando) - 4.3 paréntesis aritméticos
((...))
- 4.4 expansión aritmética
$((...))
- 4.5 uno o varios de los combinados
Entonces, eche un vistazo a la definición endOfInput
y cómo se usa en el encabezado de la while
declaración. ¿Cuál podría ser el problema, por qué hay un bucle infinito? Quiero decir, las otras dos funciones sí isWord?
funcionan.
¿Cómo debería ser la definición de mi función endOfInput
y cómo debería verse la prueba y la concatenación de la misma en la while
declaración para que se evalúe correctamente?
Editar: ilkkachu quería que publicara un "ejemplo mínimo, completo y verificable"
Espero que lo siguiente sea suficiente.
Primero llamo get_installed_gems_with_versions
para obtener todas las gemas instaladas y sus versiones en una matriz asociativa. Ahora tengo la matriz asociativa global installedGems
.
Entonces quieroanalizar gramaticalmentela opción --gems llamando parse_gems_with_versions
, que a su vez llama parseGemVersions
para analizar una selección de las gemas instaladas y su versión en$chosenGemVersions
Dejé de lado el código de algunas pruebas que son relevantes para la parte de análisis pero no para el problema actual si el ciclo while no funciona.
Así que aquí está el código.
get_installed_gems_with_versions () {
unset installedGems
declare -Ag installedGems
local last_key=""
local values=""
while read -r line; do
line=${line##*/}
KEY="${line%-*}"
VALUE="${line##*-}"
if [ -z ${installedGems[$KEY]+x} ]; then
echo "key $KEY doesn't yet exist."
echo "append value $VALUE to key $KEY"
installedGems[$KEY]="$VALUE"
continue
else
echo "key already exists"
echo "append value $VALUE to $KEY if not already exists"
installedGems[$KEY]="${installedGems[$KEY]} $VALUE"
echo "result: ${installedGems[$KEY]}"
fi
done < <(find $directory -maxdepth 1 -type d -regextype posix-extended -regex "^${directory}\/[a-zA-Z0-9]+([-_]?[a-zA-Z0-9]+)*-[0-9]{1,3}(.[0-9]{1,3}){,3}\$")
}
parseGemVersions () {
local version_list
declare -Ag chosenGemVersions
while [[ "$current_token_index" -le "$ARGC" ]] && [[ "$current_token" =~ ^[0-9]{1,2}(\.[0-9]{,2})*$ ]]; do
if versionOfGemInstalled? $gem $current_token; then
if [ -z ${chosenGemVersions[$gem]+x} ]; then
chosenGemVersions[$gem]=$current_token
# continue
else
chosenGemVersions[$gem]="${chosenGemVersions[$gem]} $current_token"
fi
else
parsing_error! "While parsing function $FUNCNAME, current version number $current_token is not installed!"
fi
echo "result: ${chosenGemVersions[$gem]}"
eat!
done
}
parse_gems_with_versions () {
# option --gems
# --gems gem-name-1 1.1.1 1.2.1 1.2.5 gem-name-2 latest gem-name-3 ...
unset gem
unset chosenGemVersions
gem=""
declare -Ag chosenGemVersions
while [ $current_token_index -le $ARGC ] && [[ "$current_token" =~ ^[a-zA-Z0-9_-]+$ ]] || ! [[ "$current_token" =~ ^[0-9]{1,2}(\.[0-9]{,2})*$ ]]; do
if isWord? $current_token && [ ! "$current_token" = "latest" ]; then
if isWord? $current_token; then
if gemInstalled? $current_token; then
# We can conjecture that the word token is in fact a gems' name
gem=$current_token
local version_list
if isWord? $current_token && [ "$current_token" = "latest" ]; then
version_list=(${installedGems[$gem]})
chosenGemVersions[$gem]="${version_list[${#version_list} -1]}"
else
# gets list chosenGemVersions
parseGemVersions
fi
else
parsing_error! "Gem $token_name not installed!"
fi
fi
else
parsing_error! "While parsing function $FUNCNAME, "latest" is not a gemname!"
fi
eat!
done
}
Respuesta1
Creo que su problema radica principalmente en no entender el tipo booleano en los scripts de Shell.
Básicamente puedes reemplazar las primeras funciones auxiliares con estas:
#!/bin/bash
set -o nounset
set -o errexit
is_word ()
{
local pattern='^[a-zA-Z0-9_-]+$'
[[ "$1" =~ $pattern ]]
}
is_version_number ()
{
local pattern='^[0-9]{1,2}(\.[0-9]{,2})*$'
[[ "$1" =~ $pattern ]]
}
# testing is_word ()
if is_word 'bla01'; then
echo IS word
else
echo NOT word
fi
# testing is_version_number ()
if is_version_number '1.1'; then
echo IS version number
else
echo NOT version number
fi
Verá, no intente repetir verdadero/falso ni nada por el estilo, el comando de prueba ( [..]
o [[..]]
) en sí mismo devuelve un valor booleano adecuado (en realidad, un número entero), nunca use sus propias construcciones verdadero/falso.
Además, utilice siempre ShellCheck (https://www.shellcheck.net/) para proteger sus scripts o depurarlos.
Un buen hábito también es utilizar set -o nounset
( set -u
) y set -o errexit
( set -e
).
Con la versión actualizada de su pregunta, veo que probablemente esté acostumbrado a la programación en C. No existe $ARGC
ni $ARGV
en scripts de shell. Utilice estos en su lugar:
ARGC
(recuento de argumentos):"$#"
ARGV
(matriz de argumentos):"$@"
Por ejemplo, podrías hacer esto:
eat_args ()
{
for arg in "$@"; do
current_char=${arg:0:1}
echo "$current_char"
done
}
eat_args "$@"
lo cual no tiene mucho sentido para mí, ya que imprime la primera letra de cada argumento dado. Saludos y buena suerte.
Respuesta2
La solución es agrupar las condiciones conectadas a través del ||
operador.
while [[ $current_token_index -le $ARGC ]] && { [[ "$current_token" =~ ^[a-zA-Z0-9_-]+$ ]] || ! [[ "$current_token" =~ ^[0-9]{1,2}(\.[0-9]{,2})*$ ]]; }; do