簡體   English   中英

為什么 Bash `(())` 在 `[[]]` 中不起作用?

[英]Why doesn't Bash `(())` work inside `[[]]`?

[[ " stop start status " =~ " $2 " && (($#<3)) ]] || { echo "Usage $0 file_name command"; exit 1;}

我經常使用上述解決方案來檢查我的 Bash 腳本的輸入范圍。

現在我意識到擴展的算術表達式(())看起來像是被抑制在雙括號[[]]內。

為了說明問題:

a=start; n=1; [[ " stop start status " =~ " $a " && (($n<3)) ]] && echo ok || echo bad
ok
a=start; n=5; [[ " stop start status " =~ " $a " && (($n<3)) ]] && echo ok || echo bad
bad

# But:
a=start; n=100; [[ " stop start status " =~ " $a " && (($n<3)) ]] && echo ok || echo bad
ok

上述結果是錯誤的,因為如果將它們視為數字,則 n 不小於 3。 這是正確的解決方案:

a=start; n=100; [[ " stop start status " =~ " $a " ]] && (($n<3)) && echo ok || echo bad
bad
a=start; n=1; [[ " stop start status " =~ " $a " ]] && (($n<3)) && echo ok || echo bad
ok

[[..]]的 GNU bash 手冊頁解釋說,運算符運行條件表達式和

根據條件表達式expression的計算返回狀態01 表達式由下面Bash 條件表達式中描述的基元組成。

但是算術運算符不是[[..]]內支持的條件表達式( primaries )的一部分,這意味着表達式被迫作為字符串比較運行,即

(( $n < 3))

不是在算術上下文中運行,而是像普通的字典(字符串)比較一樣

[[ 100 < 3 ]] 

這將始終為真,因為100的 ASCII 值出現在3之前

但是如果使用-lt-gt ,則支持[[..]]內部算術運算

arg1 OP arg2

OP 是-eq-ne-lt-le-gt-ge之一。 如果arg1分別等於、不等於、小於、小於或等於、大於或大於或等於arg2 ,則這些算術二元運算符返回 true。

所以你有沒有把你的表達寫成

a=start; n=100; [[ " stop start status " =~ " $a " && $n -lt 3 ]] && echo ok || echo bad
bad

它會按預期工作。

或者,即使您通過在((..))之前添加$前綴來強制使用算術表達式並將其編寫如下(注意 bash 在[[..]]內沒有記錄$((..))的行為) . 可能的預期行為是在評估[[..]]之前擴展算術表達式,並且在字符串上下文中將結果 output 評估為[[ 0 ]] ,這意味着非空字符串。

a=start; n=5; [[ " stop start status " =~ " $a " && $(( $n < 3 )) ]] && echo ok || echo bad

結果看起來仍然很糟糕,因為[[..]]內的算術表達式分解為一元字符串而不是空比較表達式

$(( 5 < 3 ))
0
[[ -n 0 ]]

算術評估的結果0 (false) 被測試運算符視為非零實體,並在&&的右側斷言 true。 這同樣適用於其他情況,例如說n=1

$(( 1 < 3 ))
1
[[ -n 1 ]]

長話短說,在[[..]]中使用正確的操作數進行算術運算。

((是引入算術語句的“關鍵字”。但是,在[[內部,您不能使用其他語句。但是,您可以使用括號對表達式進行分組,這就是((... ))的含義:冗余“雙組”。由於<&&的優先級,以下都是等價的:

  • [[ " stop start status " =~ " $2 " && (($#<3)) ]]
  • [[ " stop start status " =~ " $2 " && ($#<3) ]]
  • [[ " stop start status " =~ " $2 " && $#<3 ]]

如果您想要 integer 比較,請使用-lt而不是< ,但您也不需要將所有內容都放入[[... ]] 您可以在命令列表中同時使用條件語句和算術語句。

{ [[ " stop start status " =~ " $2 " ]] && (($#<3)) ; } || { echo "Usage $0 file_name command"; exit 1;}

在這種情況下, ... &&... ||...將按照您期望的方式工作,但通常情況並非如此。 更喜歡if語句。

if [[ " stop start status " =~ " $2 " ]] && (($#<3)); then
  echo "Usage $0 file_name command"
  exit 1
fi

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM