Instrução While: Não é possível fazer com que o composto de múltiplas condições funcione

Instrução While: Não é possível fazer com que o composto de múltiplas condições funcione

Tento analisar argumentos por um recursive descentesquema.

As whileinstruções chamam uma eat!função após cada execução para obter o próximo $current_tokenprocesso.

O problema agora é que assim entra em loop infinito.

Para ser exato, existem dois loops while na função.

Um deles ou ambos não estão funcionando porque o composto está sintaticamente (ou logicamente?) Errado.

Você poderia verificar o que pode estar errado com o código a seguir nos vários níveis possíveis?

1)

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

Além disso, o que é correto? Colocar as funções de verificação em uma substituição de comando ou não?

Será que o curto-circuito também impede o endOfInput?-test ?

Porque isso deveria interromper absolutamente o loop.

A pedido de ilkkachu

O código das funções de teste:

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
}

Presumo que ele produza outro comando, já que você o está usando em uma substituição de comando.

Não, foi apenas uma tentativa porque não sei se posso ou não simplesmente chamar as funções na condicional sem substituição de comando.

Para completar, também adiciono a eat!função.

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}
}

E as condições podem ser melhor formuladas com test/ [( [[)?

O ponto de exclamação poderia ser o ponto de falha?

O seguinte parece estar sintaticamente errado:

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

Do colchete [recebo a mensagem de erro

[: endOfInput: integral expression expected (translation)

Responder1

Se uma função retornar 0 para verdadeiro e qualquer outra coisa para falso, você poderá usá-la diretamente:

my-true () {
    return 0
}

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

Se não gerar nada quando for falso, mas gerar algo quando for verdadeiro, use a substituição de comando

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

Para encadear as funções no primeiro caso, você pode usar os operadores &&e ||:

while func1 && func2 ; do

Neste último caso, você pode usar concatenação em vez de ||:

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

Observe que neste último caso, você não pode modificar variáveis ​​globais na função, pois ela é chamada em um subshell.

Aliás, usar ?o nome de uma função não é proibido, mas pode causar problemas, pois é um curinga:

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?

!é o caractere de expansão do histórico padrão, mas é relevante principalmente apenas para shells interativos.

Responder2

Você poderia verificar o que pode estar errado com o código a seguir nos vários níveis possíveis?

Ok, tudo bem.

Além disso, o que é correto? Colocar as funções de verificação em uma substituição de comando ou não?

Uma substituição de comando leva qualquer que seja o comando dentroestampase coloca isso na linha de comando. (Depois de dividir e globbing, se a expansão não estiver entre aspas.) Então, se você tiver

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

então a substituição do comando seria produzida truee isso seria executado na parte da condição da ifinstrução -, e o status de saída dele seria usado para determinar se a ramificação principal da ifinstrução é executada. Seria, neste caso.

Mas isso é bobagem. Você poderia simplesmente retornar o status de saída diretamente, sem passar por uma substituição de comando (que no Bash envolve uma bifurcação):

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

Ou você pode simplesmente encerrar a função com true, já que o status de saída do último comando é usado como o status de saída da função.

Então você poderia escrever:

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

ou mesmo:

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

Observe que você precisa citar a variável, por todos os motivos usuais (ou seja, divisão e globbing de palavras).

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

Aqui, observe que a a || b && cconstrução agrupa como (a || b) && c, ou seja, ela é executada da esquerda para a direita. Ao contrário das linguagens de programação reais, onde umeoperador tem maior precedência do queou(o que significa que seria a || (b && c)em vez disso). Então, verifique qual você precisa.

Além disso, embora com as configurações padrão do Bash, um nome como esse isWord?talvez não seja um problema com muita frequência, ainda é um globo. Se você tiver um arquivo ou arquivos correspondentes no diretório atual, ele será expandido para esses nomes de arquivo. E se você passar a usar failglobor nullglob, eles também afetarão isso. Ex. com failglob:

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

Você precisaria citá-lo para não se comportar como um globo. Observe que no Zsh, um glob incomparável falha por padrão.

O estrondo como último caractere do nome de uma função não deveria importar. Ele poderia atingir a expansão do histórico se estivesse no início ou no meio do nome, e o shell tivesse a expansão do histórico ativada (os shells interativos fazem isso por padrão, os scripts não).

Será que o curto-circuito também impede o endOfInput?-test ?

Se você tiver algo como while bar && ! endOfInput?; do, então sim, se barno lado esquerdo retornar um status falso, o resultado doejá é conhecido e o lado direito é ignorado. Da mesma forma para ||um retorno verdadeiro. Isso é o que &&e ||faça.

Mas você não forneceu nenhum contexto para esta questão, ou seja, por que seria importante para você se a função não fosse chamada, por isso é difícil pensar em maneiras melhores de fazer isso.

informação relacionada