Declaración while: No se puede lograr que funcione el compuesto de múltiples condiciones

Declaración while: No se puede lograr que funcione el compuesto de múltiples condiciones

Intento analizar argumentos según un recursive descentesquema.

Las whiledeclaraciones llaman a una eat!función después de cada ejecución para que se $current_tokenprocese la siguiente.

El problema ahora es que así entra en un bucle infinito.

Para ser exactos, hay dos bucles while en la función.

Uno de ellos o ambos no funcionan porque el compuesto es sintácticamente (¿o lógicamente?) incorrecto.

¿Podría comprobar qué podría estar mal con el siguiente código en todos los niveles posibles?

1)

while $(isWord? $current_token) || ! $(isVersionNumber? $current_token) && ! $(endOfInput?); do
while isVersionNumber? $current_token && ! endOfInput?; do

Además ¿qué es correcto? ¿Poner las funciones de verificación en una sustitución de comando o no?

¿Podría ser que un cortocircuito también impida la endOfInput?prueba?

Porque eso debería detener por completo el ciclo.

A petición de ilkkachu

El código de las funciones de prueba:

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=1

function endOfInput? {
 if [ $current_token_index -ge $ARGC ]; then
    EO_ARGS=0
 fi
 echo $EO_ARGS
}

Supongo que genera otro comando, ya que lo estás usando en una sustitución de comando.

No, fue solo un intento porque no sé si puedo llamar a las funciones en el condicional sin sustitución de comandos.

Para completar, también agrego la eat!función.

eat! () {
    
if [[ ! $(($current_char_index + 1)) -gt $ARGC ]]; then
  current_token=${ARGV[$current_token_index]}
  ((current_token_index += 1))

  current_char=${current_token:0:1}
}

¿Y es posible que las condiciones se formulen mejor con test/ [( [[)?

¿Podría el signo de exclamación ser el punto del fracaso?

Lo siguiente parece ser sintácticamente incorrecto:

while [ endOfInput -eq 1 ] && isWord? "$current_token" || ! isVersionNumber? "$current_token"; do

Del corchete [me sale el mensaje de error

[: endOfInput: integral expression expected (translation)

Respuesta1

Si una función devuelve 0 para verdadero y cualquier otra cosa para falso, puedes usarla directamente:

my-true () {
    return 0
}

while my-true ; do
    echo Infinite loop...
done

Si no genera nada cuando es falso pero genera algo cuando es verdadero, use la sustitución de comandos

my-true () {
    echo OK
}
while [ "$(my-true)" ] ; do
    echo Infinite loop...
done

Para encadenar las funciones en el primer caso, puedes usar los operadores &&y :||

while func1 && func2 ; do

En el último caso, puedes usar concatenación en lugar de ||:

while [ "$(func1 ; func2)" ] && [ "$(func3)" ]

Tenga en cuenta que en el último caso, no puede modificar las variables globales en la función, como se llama en una subcapa.

Por cierto, no está prohibido usarlo ?en el nombre de una función, pero puede causar problemas, ya que es un comodín:

abc? () {
    return $1
}
abc? 0   # Everything OK.
touch abcX
abc? 0   # line 9: ./abcX: Permission denied
abc\? 0  # This works, but where's the charm?

!es el carácter de expansión del historial predeterminado, pero es principalmente relevante solo para shells interactivos.

Respuesta2

¿Podría comprobar qué podría estar mal con el siguiente código en todos los niveles posibles?

Está bien.

Además ¿qué es correcto? ¿Poner las funciones de verificación en una sustitución de comando o no?

Una sustitución de comando toma el comando dentrohuellas dactilaresy lo pone en la línea de comando. (Después de dividir y agrupar, si la expansión no estaba entre comillas). Entonces, si tiene

foo() {
    echo true
}
if $(foo); then
    echo hello
fi

entonces la sustitución del comando se produciría truey se ejecutaría en la parte de condición de la ifdeclaración, y el estado de salida de la misma se usaría para determinar si se ifejecuta la rama principal. Lo sería, en este caso.

Pero eso es una tontería. Podrías simplemente devolver el estado de salida directamente, sin pasar por una sustitución de comando (que en Bash implica una bifurcación):

foo() {
    return 0
}
if foo; then
    echo hello
fi

O simplemente podría finalizar la función con true, ya que el estado de salida del último comando se utiliza como estado de salida de la función.

Entonces podrías escribir:

isWord? () {
    local pattern="^[a-zA-Z0-9_-]+$"
    if [[ $1 =~ $pattern ]]; then
        return 0
    else
        return 1
    fi
}
while isWord? "$current_token"

o incluso:

isWord? () {
    local pattern="^[a-zA-Z0-9_-]+$"
    [[ $1 =~ $pattern ]]
}
while isWord? "$current_token"

Tenga en cuenta que debe citar la variable, por todas las razones habituales (es decir, división de palabras y globbing).

while $(isWord? $current_token) || ! $(isVersionNumber? $current_token) && ! $(endOfInput?); do

Aquí, tenga en cuenta que la a || b && cconstrucción se agrupa como (a || b) && c, es decir, se ejecuta de izquierda a derecha. A diferencia de los lenguajes de programación reales donde unyEl operador tiene mayor prioridad queo(lo que significa que sería a || (b && c)en su lugar). Entonces, comprueba cuál necesitas.

Además, aunque con la configuración predeterminada en Bash, un nombre como isWord?quizás no sea un problema con demasiada frecuencia, sigue siendo un pegote. Si tiene un archivo o archivos que coincidan con él en el directorio actual, se expandirá a esos nombres de archivo. Y si cambias a usar failglobo nullglob, eso también afectará. Por ejemplo con failglob:

$ foo? () { echo hi; }
$ foo?
bash: no match: foo?
$ "foo?"
hi

Tendrías que citarlo para que no se comporte como un pegote. Tenga en cuenta que en Zsh, un globo que no coincide falla de forma predeterminada.

La explosión como último carácter del nombre de una función no debería importar. Podría alcanzar la expansión del historial si estuviera al principio o en el medio del nombre, y el shell tuviera la expansión del historial activada (los shells interactivos lo hacen de forma predeterminada, los scripts no).

¿Podría ser que el cortocircuito también impida la prueba endOfInput?

Si tiene algo como while bar && ! endOfInput?; do, entonces sí, si baren el lado izquierdo devuelve un estado falso, el resultado de layya se conoce y se omite el lado derecho. Lo mismo ocurre ||con una devolución veraz. Eso es lo que &&y ||hacer.

Pero no le dio ningún contexto a esta pregunta, es decir, por qué le importaría si no se llama a la función, por lo que es difícil pensar en mejores formas de hacerlo.

información relacionada