Bash while condición: ¿Mi prueba es correcta? El bucle infinito implica

Bash while condición: ¿Mi prueba es correcta? El bucle infinito implica

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 descentmanera.

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 whiledeclaraciones en mi script, que son problemáticas, porque obviamente la endOfInputfunción no tiene influencia para finalizar el ciclo while.

Hurgaba y a veces ni siquiera entraba en el bucle.

Entonces el exterior while-statementes 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 endOfInputfunció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 whilecabezales de bucle publicados anteriormente es que no se ingresan o no se detienen. Dependiendo de cómo pruebo endOfInputo 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 truey falsecomo literales
    • 2.2.3 0por truey 1parafalse
  • 3 ¿Cómo se devuelve correctamente este valor?

    • 3.1 porreturn
    • 3.2 porexit
    • 3.3 porecho
  • 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 endOfInputy cómo se usa en el encabezado de la whiledeclaració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 endOfInputy cómo debería verse la prueba y la concatenación de la misma en la whiledeclaració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_versionspara 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 parseGemVersionspara 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 $ARGCni $ARGVen 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 

información relacionada