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"
declare
和readonly
的r
選項用於明確聲明變量是只讀的 。 希望目的很明確。
POSIX (便攜式操作系統接口)
我想念這裡的關鍵點,這是可移植性。 這就是為什麼我的頭本身有POSIX 。
從本質上講,所有投票答案都是正確的,除了他們特別是BASH以外。
所以基本上,我只想添加更多關於可移植性的信息。
[
和]
括號中的[ "$var" = true ]
不是必需的,您可以省略它們並直接使用test
命令:test "$var" = true && CodeIfTrue || CodeIfFalse
想像一下這些詞的
true
和false
對殼體意味著什麼,請親自測試一下: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不能解釋它。 我希望您能夠了解如何使用不含引號的正確關鍵字。
但之前的答案中沒有人說過。
這是什麼意思? 好吧,好幾件事。
您應該習慣布爾關鍵字實際上被視為數字,即
true
=0
和false
=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
您必須遵守紀律,您的
testvar
必須始終設置為$TRUE
或$FALSE
。在
((
...))
比較器中,您不需要前面的$
,這使得它更具可讀性。我可以使用
((
...))
因為$TRUE=1
和$FALSE=0
,即數字值。缺點是不得不偶爾使用
$
: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在每種情況下所做的事情:
沒有括號:
- 將變量
$the_world_is_flat
展開為字符串"true"
。 - 嘗試將字符串
"true"
解析為命令。 - 查找並運行
true
命令(內建版或/bin/true
,具體取決於bash版本)。 - 將
true
命令(始終為0)的退出代碼與0進行比較。回想一下,在大多數shell中,退出代碼0表示成功,其他任何代碼表示失敗。 - 由於退出代碼為0(成功),執行
if
語句的then
子句
括號:
- 將變量
$the_world_is_flat
展開為字符串"true"
。 - 解析現在完全展開的條件表達式,其形式為
string1 = string2
。=
運算符是bash的字符串比較運算符 。 所以... - 對
"true"
和"true"
進行字符串比較。 - 是的,這兩個字符串是相同的,所以條件的值是真的。
- 執行
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其他比較運算符 (向下滾動到討論=
和==
)。