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。如果目前目錄中有一個或多個與其相符的文件,它將擴展到這些文件名。如果您改用failglobor nullglob,它們也會影響它。例如failglob

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

您需要引用它才能使其不表現為全域。請注意,在 Zsh 中,預設情況下不符合的 glob 會失敗。

作為函數名稱最後一個字元的感嘆號應該不重要。如果它位於名稱的開頭或中間,且 shell 啟用了歷史擴充(預設情況下互動式 shell 執行此操作,腳本則沒有),則它可能會遇到歷史擴充。

難道短路也會阻止 endOfInput?-test 嗎?

如果你有類似的東西while bar && ! endOfInput?; do,那麼是的,如果bar左側返回一個虛假狀態,則結果是已知的,並且跳過右側。同樣,對於||和 真實的返回。這就是&&所做||的。

但是您沒有給出這個問題的任何上下文,為什麼如果不呼叫該函數對您來說很重要,所以很難想出更好的方法來做到這一點。

相關內容