bash if [false]; trueを返します


151

今週bashを学んでいて、思わぬ障害に遭遇しました。

#!/bin/sh

if [ false ]; then
    echo "True"
else
    echo "False"
fi

条件がそうでないように見える場合でも、これは常にTrueを出力します。ブラケットを削除すると機能しますが[]、理由がわかりません。


3
ここから始めましょう。
キーヤー2013年

10
ところで、で始まるスクリプト#!/bin/shはbashスクリプトではなく、POSIX shスクリプトです。システム上のPOSIX shインタープリターがbashによって提供されている場合でも、一連の拡張機能をオフにします。bashスクリプトを記述したい場合は、#!/bin/bashまたはローカルで適切な同等のもの#!/usr/bin/env bashを使用します(PATHで最初のbashインタープリターを使用します)。
Charles Duffy、

これも影響し[[ false ]]ます。
CMCDragonkai 2015

回答:


192

コマンドを実行するのではなく、引数「false」を指定して[(別名test)コマンドを実行していfalseます。「false」は空ではない文字列なので、testコマンドは常に成功します。コマンドを実際に実行するには、コマンドをドロップし[ます。

if false; then
   echo "True"
else
   echo "False"
fi

4
falseがコマンド、または文字列でさえあるという考えは私には奇妙に思えますが、私はそれがうまくいくと思います。ありがとう。
2013年

31
bashブールデータ型がないため、trueとfalseを表すキーワードはありません。このifステートメントは、指定したコマンドが成功したか失敗したかを確認するだけです。testコマンドは、式を取り、式がtrueの場合成功します。空でない文字列は、他のほとんどのプログラミング言語と同様に、真と評価される式です。false常に失敗するコマンドです。(類推によってtrue、常に成功するコマンドです。)
chepner 2013年

2
if [[ false ]];trueも返します。同様にif [[ 0 ]];。またif [[ /usr/bin/false ]];、ブロックもスキップしません。falseまたは0をゼロ以外に評価するにはどうすればよいですか?くそーこれはイライラさせられます。問題がある場合は、OS X 10.8。バッシュ3.
jww 2016年

7
falseブール定数や0整数リテラルと間違えないでください。どちらも通常の文字列です。ハイフンで始まらない文字列の場合[[ x ]]と同等です。どのコンテキストにブール定数はありません。整数のような外観は、このような内部として扱われていることを文字列またはなどの演算子でまたは内部。[[ -n x ]]xbash(( ... ))-eq-lt[[ ... ]]
chepner 2016年

2
@ tbc0、何かがどのように読み取られるかよりも多くの懸念が手元にあります。変数が完全に制御下にないコードによって設定されている場合(または、異常なファイル名などによって外部から操作できる場合)は、if $predicate悪意のあるコードを実行if [[ $predicate ]]できない方法で簡単に悪用される可能性があります。
Charles Duffy

55

Bashのクイックブールプライマー

if文は、引数としてコマンドを受け取り(そうであるように&&||など)。コマンドの整数の結果コードはブール値(0 / null = true、1 / else = false)として解釈されます。

このtestステートメントは、演算子とオペランドを引数として取り、と同じ形式で結果コードを返しますiftestステートメントのエイリアスはです[。これはif、より複雑な比較を実行するためにしばしば使用されます。

trueそしてfalseステートメントは、何もしないし、結果コードを返します(0と1、それぞれ)。そのため、Bashではブールリテラルとして使用できます。しかし、文字列として解釈される場所にステートメントを配置すると、問題が発生します。あなたの場合:

if [ foo ]; then ... # "if the string 'foo' is non-empty, return true"
if foo; then ...     # "if the command foo succeeds, return true"

そう:

if [ true  ] ; then echo "This text will always appear." ; fi;
if [ false ] ; then echo "This text will always appear." ; fi;
if true      ; then echo "This text will always appear." ; fi;
if false     ; then echo "This text will never appear."  ; fi;

これはecho '$foo'vsのようなものに似ていますecho "$foo"

testステートメントを使用する場合、結果は使用する演算子によって異なります。

if [ "$foo" = "$bar" ]   # true if the string values of $foo and $bar are equal
if [ "$foo" -eq "$bar" ] # true if the integer values of $foo and $bar are equal
if [ -f "$foo" ]         # true if $foo is a file that exists (by path)
if [ "$foo" ]            # true if $foo evaluates to a non-empty string
if foo                   # true if foo, as a command/subroutine,
                         # evaluates to true/success (returns 0 or null)

つまり、単に合格/不合格(別名「true」/「false」)としてテストしたい場合は、大括弧なしでコマンドをifor &&などのステートメントに渡します。複雑な比較の場合は、適切な演算子で角かっこを使用します。

そして、はい、私はBashにネイティブのブール型などがないことを知っています。そしてif[それtrueは技術的には「コマンド」であり、「ステートメント」ではありません。これは非常に基本的な機能説明です。


1
ちなみに、コードで1/0をTrue / Falseとして使用したい場合は、orまたは(undefined)のif (( $x )); then echo "Hello"; fiメッセージx=1が表示されますが、forは表示されません。x=0x=
ジョナサンH

3

私は次のようなものを実行することでいくつかの基本的なロジックを実行できることがわかりました:

A=true
B=true
if ($A && $B); then
    C=true
else
    C=false
fi
echo $C

6
一つはできるが、それは本当に悪い考えです。( $A && $B )は驚くほど非効率的な操作です-を呼び出してサブプロセスを生成しfork()、の内容を文字列分割して$A要素のリスト(この場合はサイズ1)を生成し、各要素をグロブ(この場合は1)として評価しますそれ自体が評価されます)。次に、結果をコマンド(この場合は組み込みコマンド)として実行します。
Charles Duffy

3
あなたの場合はさらに、trueおよびfalse文字列がどこか別の場所から来ている、リスクは、彼らが実際に以外のコンテンツが含まれている可能性があることがありますtruefalse、とあなたは、任意のコマンドの実行など、予期せぬ振る舞いを取得し、可能性があります。
Charles Duffy

6
それは、ずっとだ多くの書き込みに、より安全A=1; B=1かつif (( A && B ))-または-数値として扱う[[ $A && $B ]]偽と真などの非空の文字列として空の文字列を扱うています、。
Charles Duffy、

3
(私は上記のすべて大文字の変数名を使用して、回答によって確立された規則に従っていることに注意してください-POSIXは実際、環境変数とシェルの組み込みに使用するすべて大文字の名前を明示的に指定し、アプリケーションで使用する小文字の名前を予約しています。特にシェル変数を設定すると、同じ名前の環境変数が上書きされるため、将来のOS環境との競合を回避するために、自分のスクリプトでその規則に従うことは常に理想的です)。
Charles Duffy

洞察をありがとう!
ロドリゴ

2

true / falseを使用すると、ブラケットの乱雑さが取り除かれます...

#! /bin/bash    
#  true_or_false.bash

[ "$(basename $0)" == "bash" ] && sourced=true || sourced=false

$sourced && echo "SOURCED"
$sourced || echo "CALLED"

# Just an alternate way:
! $sourced  &&  echo "CALLED " ||  echo "SOURCED"

$sourced && return || exit

これは役に立ちましたが、ubuntu 18.04では$ 0は "-bash"であり、basenameコマンドはエラーをスローしました。そのテストを変更して[[ "$0" =~ "bash" ]] 、スクリプトを機能させました。
WiringHarness
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.