While 语句:无法使多个条件复合起作用

While 语句:无法使多个条件复合起作用

我尝试通过recursive descent模式解析参数。

这些语句在每次执行后while调用一个函数来进行下一个处理。eat!$current_token

现在的问题是这样就进入了无限循环。

准确的说,函数中有两个while循环。

它们之一或两者都不起作用,因为该复合词在语法上(或逻辑上?)是错误的。

您能否检查以下代码在多个可能级别上可能存在的问题?

1)

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

另外什么是正确的?是否将检查函数放入命令替换中?

难道短路也会阻止endOfInput?-test 吗?

因为那绝对应该停止循环。

应伊尔卡丘的要求

测试函数的代码:

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
}

我假设它输出另一个命令,因为您在命令替换中使用它?

不,这只是一次尝试,因为我不知道是否可以在不使用命令替换的情况下调用条件中的函数。

为了完整起见,我还添加了该eat!功能。

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

条件可以用test/ [( [[) 更好地表述吗?

感叹号可能是失败点吗?

以下似乎在语法上是错误的:

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

从括号中[我得到错误消息

[: endOfInput: integral expression expected (translation)

答案1

如果函数返回 0 表示 true,其他值表示 false,则可以直接使用它:

my-true () {
    return 0
}

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

如果为 false 时不输出任何内容,但为 true 时输出某些内容,请使用命令替换

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

要链接前一种情况下的函数,您可以使用&&||运算符:

while func1 && func2 ; do

在后一种情况下,您可以使用串联而不是||

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

请注意,在后一种情况下,您无法修改函数中的全局变量,因为它是在子 shell 中调用的。

顺便说一句,在函数名称中使用?是不被禁止的,但它可能会导致问题,因为它是通配符:

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?

!是默认的历史扩展字符,但这主要仅与交互式 shell 相关。

答案2

您能否检查以下代码在多个可能级别上可能存在的问题?

好的。

另外什么是正确的?是否将检查函数放入命令替换中?

命令替换采用里面的命令印刷,并将其放在命令行上。 (在分割和通配之后,如果扩展未加引号。)所以如果你有

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

然后命令替换将产生true并且将在 - 语句的条件部分中运行if,并且它的退出状态将用于确定主分支是否运行if。在这种情况下,会的。

但这很愚蠢。您可以直接返回退出状态,而不需要通过命令替换(在 Bash 中涉及 fork):

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

或者您可以使用 结束函数true,因为最后一个命令的退出状态用作函数的退出状态。

所以你可以写:

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

甚至:

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

请注意,出于所有常见原因(即分词和通配符),您需要引用该变量。

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

这里,请注意a || b && c构造组为(a || b) && c,即它从左到右执行。与真实的编程语言不同,运算符的优先级高于或者(意思是它会是a || (b && c)相反)。因此,请检查您需要哪一个。

另外,虽然使用 Bash 的默认设置,类似的名称isWord?可能并不是经常出现的问题,但它仍然是一个 glob。如果当前目录中有一个或多个与其匹配的文件,它将扩展到这些文件名。如果您改用failglobnullglob,它们也会影响它。例如failglob

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

您需要引用它才能使其不表现为全局。请注意,在 Zsh 中,默认情况下不匹配的 glob 会失败。

作为函数名称最后一个字符的感叹号应该不重要。如果它位于名称的开头或中间,并且 shell 启用了历史扩展(默认情况下交互式 shell 执行此操作,脚本则没有),则它可能会遇到历史扩展。

难道短路也会阻止 endOfInput?-test 吗?

如果你有类似的东西while bar && ! endOfInput?; do,那么是的,如果bar左侧返回一个虚假状态,则结果是已知的,并且跳过右侧。同样,对于||和 真实的返回。这就是&&所做||的。

但是您没有给出这个问题的任何上下文,即为什么如果不调用该函数对您来说很重要,所以很难想出更好的方法来做到这一点。

相关内容