bash - loop - shell script if




如何在shell腳本中聲明和使用布爾變量? (10)

我嘗試使用以下語法在shell腳本中聲明一個布爾變量:

variable=$false

variable=$true

它是否正確? 另外,如果我想更新該變量,我會使用相同的語法? 最後,使用布爾變量作為正確表達式的語法如下:

if [ $variable ]

if [ !$variable ]

如何在shell腳本中聲明和使用布爾變量?

與許多其他編程語言不同,Bash不按“類型”分隔變量。 [1]

所以答案很清楚。 bash中沒有boolean variable 。 但是:

使用聲明語句,我們可以將賦值賦給變量。 [2]

#!/bin/bash
declare -ir BOOL=(0 1) #remember BOOL can't be unset till this shell terminate
readonly false=${BOOL[0]}
readonly true=${BOOL[1]}
#same as declare -ir false=0 true=1
((true)) && echo "True"
((false)) && echo "False"
((!true)) && echo "Not True"
((!false)) && echo "Not false"

declarereadonlyr選項用於明確聲明變量是只讀的 。 希望目的很明確。


POSIX (便攜式操作系統接口)

我想念這裡的關鍵點,這是可移植性。 這就是為什麼我的頭本身有POSIX

從本質上講,所有投票答案都是正確的,除了他們特別是BASH以外。

所以基本上,我只想添加更多關於可移植性的信息。

  1. []括號中的[ "$var" = true ]不是必需的,您可以省略它們並直接使用test命令:

    test "$var" = true && CodeIfTrue || CodeIfFalse
    
  2. 想像一下這些詞的truefalse對殼體意味著什麼,請親自測試一下:

    echo $((true))
    
    0
    
    echo $((false))
    
    1
    

    但是使用引號:

    echo $(("true"))
    
    bash: "true": syntax error: operand expected (error token is ""true"")
    sh (dash): sh: 1: arithmetic expression: expecting primary: ""true""
    

    同樣的情況是:

    echo $(("false"))
    

    除了字符串外,shell不能解釋它。 我希望您能夠了解如何使用不含引號的正確關鍵字。

    但之前的答案中沒有人說過。

  3. 這是什麼意思? 好吧,好幾件事。

    • 您應該習慣布爾關鍵字實際上被視為數字,即true = 0false = 1 ,記住所有非零值都被視為false

    • 由於它們被視為數字,所以你也應該這樣對待它們,即如果你定義了變量say:

      var_a=true
      echo "$var_a"
      
       true
      

      您可以通過以下方式創建相反的值:

      var_a=$((1 - $var_a))
      echo "$var_a"
      
      1
      

      正如你自己看到的,shell在你第一次使用它的時候會打印出true字符串,但是從那以後,它們分別通過數字0或者1工作。

最後,你應該怎麼處理所有這些信息

  • 第一個好習慣是分配0代替true ; 1而不是false

  • 第二個好習慣是測試變量是否等於零:

    test "$var" -eq 0 && CodeIfTrue || CodeIfFalse
    

BASH真的把這個問題弄糊塗了, [[(($((等)

所有對待彼此的代碼空間。 我想這主要是歷史性的, bash偶爾會假裝偶爾。

大多數時候,我可以選擇一種方法並堅持下去。 在這個例子中,我傾向於聲明(最好在我可以包含在我的實際腳本中的通用庫文件中)。

TRUE=1; FALSE=0

然後我可以使用(( ... ))算術運算符進行測試。

testvar=$FALSE

if [[ -d ${does_directory_exist} ]]
then
    testvar=$TRUE;
fi

if (( testvar == TRUE )); then
    # do stuff because the directory does exist
fi
  1. 您必須遵守紀律,您的testvar必須始終設置為$TRUE$FALSE

  2. (( ... ))比較器中,您不需要前面的$ ,這使得它更具可讀性。

  3. 我可以使用(( ... ))因為$TRUE=1$FALSE=0 ,即數字值。

  4. 缺點是不得不偶爾使用$

    testvar=$TRUE
    

    這不太漂亮。

這不是一個完美的解決方案,但它涵蓋了每一個案例,我需要這樣一個測試。


if true這是一個短期執行。

# Function to test if a variable is set to "true"
_if () {
    [ "${1}" == "true" ] && return 0
    [ "${1}" == "True" ] && return 0
    [ "${1}" == "Yes" ] && return 0
    return 1
}

例1

my_boolean=true

_if ${my_boolean} && {
    echo "True Is True"
} || {
    echo "False Is False"
}

例2

my_boolean=false
! _if ${my_boolean} && echo "Not True is True"

使用算術表達式。

#!/bin/bash

false=0
true=1

((false)) && echo false
((true)) && echo true
((!false)) && echo not false
((!true)) && echo not true

輸出:

真正
不是假的


很久以前,當我們所有的東西都是sh ,依靠test程序的約定來處理布爾值,如果test在沒有參數的情況下運行時返回錯誤的退出狀態。 這使得人們可以將未設置為false的變量和設置為任何值的變量視為true。 今天,測試內置於bash ,並且通常由其一個字符別名[ (或者可用於缺少shell的可執行文件,如dolmen筆記):

FLAG="up or <set>"

if [ "$FLAG" ] ; then 
    echo 'Is true'
else 
    echo 'Is false'
fi

# unset FLAG
#    also works
FLAG=

if [ "$FLAG" ] ; then
    echo 'Continues true'
else
    echo 'Turned false'
fi

由於引用約定,腳本編寫者更喜歡使用複合命令[[模仿test但具有更好的語法:帶空格的變量不需要引用,可以使用&&|| 作為具有奇怪優先級的邏輯運算符,並且術語數沒有POSIX限制。

例如,要確定是否設置了FLAG,並且COUNT是大於1的數字:

FLAG="u p"
COUNT=3

if [[ $FLAG  && $COUNT -gt '1' ]] ; then 
    echo 'Flag up, count bigger than 1'
else 
    echo 'Nope'
fi

當需要空格,零長度字符串和空變量以及腳本需要使用多個shell時, 這些東西可能會引起混淆 。


比爾帕克被拒絕了,因為他的定義與正常的代碼慣例相反。 通常情況下,true被定義為0,false被定義為非零。 1將會失敗,9999和-1也是如此。 與函數返回值相同 - 0表示成功,任何非零表示失敗。 對不起,我還沒有投票或直接回复他。

Bash建議現在使用雙括號作為習慣而不是單括號,Mike Holt的鏈接解釋了他們工作方式的差異。 7.3。 其他比較運算符

有一件事-eq是一個數字運算符,所以有代碼

#**** NOTE *** This gives error message *****
The_world_is_flat=0;
if [ "${The_world_is_flat}" -eq true ]; then

將發出一個錯誤語句,期望一個整數表達式。 這適用於任一參數,因為它們都不是整數值。 然而,如果我們在它的周圍放上雙括號,它不會發出錯誤聲明,但會產生一個錯誤的值(在50%的可能排列中)。 它會評估為[[0 -eq true]] =成功,但也會評估為[[0 -eq false]] =成功,這是錯誤的(嗯......那個內建值是一個數值?)。

#**** NOTE *** This gives wrong output *****
The_world_is_flat=true;
if [[ "${The_world_is_flat}" -eq true ]]; then

有條件的其他排列也會給出錯誤的輸出。 基本上,任何(除了上面列出的錯誤條件)將變量設置為數值並將其與真/假內建比較,或將變量設置為真/假內建並將其與數值進行比較。 此外,任何將變量設置為true / false內建值並使用-eq進行比較的任何內容。 因此,請避免使用-eq進行布爾比較,並避免使用數值進行布爾比較。 以下是將導致無效結果的排列概要:

#With variable set as an integer and evaluating to true/false
#*** This will issue error warning and not run: *****
The_world_is_flat=0;
if [ "${The_world_is_flat}" -eq true ]; then

#With variable set as an integer and evaluating to true/false
#*** These statements will not evaluate properly: *****
The_world_is_flat=0;
if [ "${The_world_is_flat}" -eq true ]; then
#
if [[ "${The_world_is_flat}" -eq true ]]; then
#
if [ "${The_world_is_flat}" = true ]; then
#
if [[ "${The_world_is_flat}" = true ]]; then
#
if [ "${The_world_is_flat}" == true ]; then
#
if [[ "${The_world_is_flat}" == true ]]; then


#With variable set as an true/false builtin and evaluating to true/false
#*** These statements will not evaluate properly: *****
The_world_is_flat=true;
if [[ "${The_world_is_flat}" -eq true ]]; then
#
if [ "${The_world_is_flat}" = 0 ]; then
#
if [[ "${The_world_is_flat}" = 0 ]]; then
#
if [ "${The_world_is_flat}" == 0 ]; then
#
if [[ "${The_world_is_flat}" == 0 ]]; then

所以,現在到了什麼工作。 使用true / false內建函數來進行比較和評估(正如Mike Hunt所指出的,不要將它們用引號引起來)。 然後使用或者單或雙等號(=或==)以及單或雙括號([]或[[]])。 就我個人而言,我喜歡雙等號,因為它讓我想起在其他編程語言中的邏輯比較,以及雙引號,因為我喜歡打字。 所以這些工作:

#With variable set as an integer and evaluating to true/false
#*** These statements will work properly: *****
#
The_world_is_flat=true/false;
if [ "${The_world_is_flat}" = true ]; then
#
if [[ "${The_world_is_flat}" = true ]]; then
#
if [ "${The_world_is_flat}" = true ]; then
#
if [[ "${The_world_is_flat}" == true ]]; then

你有它。


為什麼不使用比真實和錯誤更好的值呢?而不是偽造布爾值並為未來的讀者留下陷阱。

例如:

build_state=success
if something-horrible; then
  build_state=failed
fi

if [[ "$build_state" == success ]]; then
  echo go home, you are done
else
  echo your head is on fire, run around in circles
fi

這是米庫最初的答案的一個改進,它解決了丹尼斯威廉姆森關於這個案例的關注,其中沒有設置變量:

the_world_is_flat=true

if ${the_world_is_flat:-false} ; then
    echo "Be careful not to fall off!"
fi

並測試該變量是否為false

if ! ${the_world_is_flat:-false} ; then
    echo "Be careful not to fall off!"
fi

關於變量中令人討厭的內容的其他情況,這是任何外部輸入饋給程序的問題。

任何外部輸入必須在信任之前進行驗證。 但是,只有在接收到輸入信息時才需要進行一次驗證。

它不需要像丹尼斯威廉姆森所說的那樣使用變量來影響程序的性能。


這裡似乎存在一些關於bash內建的誤解,更具體地說,關於bash如何擴展和解釋括號內的表達式。

miku的回答中的代碼與bash內建的true/bin/true以及true命令的任何其他風格完全沒有關係。 在這種情況下, true只不過是一個簡單的字符串,並且不會調用true命令/內建函數,既不是通過變量賦值也不是通過對條件表達式的求值。

以下代碼在功能上與miku的答案中的代碼相同:

the_world_is_flat=yeah
if [ "$the_world_is_flat" = yeah ]; then
    echo 'Be careful not to fall off!'
fi

這裡唯一的區別是,比較的四個字符是'y','e','a'和'h',而不是't','r','u'和'e'。 而已。 沒有任何嘗試調用命令或內置命名為yeah ,也沒有(在miku的例子中)當bash分析令牌為true時進行的任何特殊處理。 這只是一個字符串,而且完全是任意的。

更新(2/19/2014):在miku回答中的鏈接之後,現在我看到了一些混淆來自哪裡。 Miku的答案使用了單個括號,但是他鏈接的代碼段不使用括號。 只是:

the_world_is_flat=true
if $the_world_is_flat; then
  echo 'Be careful not to fall off!'
fi

這兩個代碼片段的行為方式都是一樣的,但括號完全改變了底層的內容。

這裡是bash在每種情況下所做的事情:

沒有括號:

  1. 將變量$the_world_is_flat展開為字符串"true"
  2. 嘗試將字符串"true"解析為命令。
  3. 查找並運行true命令(內建版或/bin/true ,具體取決於bash版本)。
  4. true命令(始終為0)的退出代碼與0進行比較。回想一下,在大多數shell中,退出代碼0表示成功,其他任何代碼表示失敗。
  5. 由於退出代碼為0(成功),執行if語句的then子句

括號:

  1. 將變量$the_world_is_flat展開為字符串"true"
  2. 解析現在完全展開的條件表達式,其形式為string1 = string2=運算符是bash的字符串比較運算符 。 所以...
  3. "true""true"進行字符串比較。
  4. 是的,這兩個字符串是相同的,所以條件的值是真的。
  5. 執行if語句的then子句。

無括號的代碼有效,因為true命令返回0的退出代碼,表示成功。 括號內的代碼起作用是因為$the_world_is_flat的值與=右側的字符串true值相同。

為了將這一點引入家庭,請考慮以下兩段代碼:

此代碼(如果以root權限運行)將重新啟動您的計算機:

var=reboot
if $var; then
  echo 'Muahahaha! You are going down!'
fi

此代碼僅顯示“Nice try”。 重新啟動命令不被調用。

var=reboot
if [ $var ]; then
  echo 'Nice try.'
fi

更新(2014年4月14日)要回答評論中關於=== AFAIK之間差異的問題,沒有區別。 ==運算符是=的bash特定的同義詞,就我所見,它們在所有上下文中都完全相同。 但是,請注意,我正在專門討論在[ ][[ ]]測試中使用的===字符串比較運算符。 我並不是建議===在bash中隨處可以互換。 例如,你顯然不能用==做變量賦值,比如var=="foo" (技術上你可以這樣做,但是var的值將是"=foo" ,因為bash沒有看到==運算符在這裡,它看到一個= (賦值)運算符,後面是字面值="foo" ,它變成了"=foo" )。

此外,儘管===可以互換,但您應該記住,這些測試的工作方式取決於您在[ ]還是[[ ]]使用它,以及操作數是否被引用。 你可以在這裡閱讀更多關於它的信息: 7.3其他比較運算符 (向下滾動到討論=== )。





sh