Bash якщо [false]; повертає правду


151

Навчався башти цього тижня і наткнувся на корч.

#!/bin/sh

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

Це завжди буде істинним, хоча, здавалося б, умова вказує інакше. Якщо я знімаю дужки, []то це працює, але я не розумію, чому.


3
Ось щось для початку.
клавішник

10
BTW, скрипт, що починається з #!/bin/shне є скриптом bash - це сценарій POSIX sh. Навіть якщо інтерпретатор POSIX sh у вашій системі надається bash, він вимикає купу розширень. Якщо ви хочете писати bash-скрипти, використовуйте #!/bin/bashйого відповідний локальний еквівалент ( #!/usr/bin/env bashщоб використовувати перший інтерпретатор bash в PATH).
Чарльз Даффі

Це [[ false ]]теж впливає .
CMCDragonkai

Відповіді:


192

Ви виконуєте команду [(aka test) з аргументом "false", а не командою false. Оскільки "false" - це не порожній рядок, testкоманда завжди вдається. Щоб насправді виконати команду, киньте [команду.

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

4
Ідея фальшивості бути командою чи навіть рядком мені здається дивною, але я думаю, вона працює. Дякую.
десять миль

31
bashне має булевих типів даних, і тому немає ключових слів, що представляють істинні та хибні. ifОператор просто перевіряє , є чи команда , яку ви дати його успіх чи невдачу. testКоманда приймає вираз і успішно , якщо вираз істинний; не порожня рядок - це вираз, який оцінюється як істинне, як і в більшості інших мов програмування. falseце команда, яка завжди виходить з ладу. (За аналогією, trueце команда, яка завжди досягає успіху.)
чепнер

2
if [[ false ]];також повертає правду. Як це робить if [[ 0 ]];. І if [[ /usr/bin/false ]];блок не пропускає. Як можна помилкове значення 0 або 0 оцінити до ненульового? Чорт забирає це. Якщо це має значення, ОС X 10.8; Bash 3.
jww

7
Не помиляйтесь falseза булевою константою або 0для цілого літералу; обидва - це просто звичайні струни. [[ x ]]еквівалентна [[ -n x ]]будь-якій рядку x, який не починається з дефісу. bashне має жодних - або логічних констант в будь-якому контексті. Рядки, схожі на цілі числа, трактуються як такі всередині (( ... ))або з операторами, такими як -eqабо -ltвсередині [[ ... ]].
чепнер

2
@ tbc0, є більше проблем, ніж просто те, як щось читається. Якщо змінні встановлюються кодом, який не повністю знаходиться під вашим контролем (або яким можна керувати зовнішньо, будь то незвичні назви файлів або іншим способом), if $predicateможна легко зловживати, щоб виконувати ворожий код таким чином, який if [[ $predicate ]]не може.
Чарльз Даффі

55

Швидкий булевий ґрунтовник для Bash

ifОператор приймає команду в якості аргументу (як &&, ||і т.д.). Цілий код результату команди інтерпретується як булева (0 / null = true, 1 / else = false).

Оператор testбере операторів і операндів як аргументи і повертає код результату в тому ж форматі, що і if. Псевдонім testтвердження є [, яке часто використовується 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'проти прогнозу 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)

Коротше кажучи , якщо ви просто хочете перевірити щось на кшталт pass / fail (так само "true" / "false"), тоді передайте команду своєму оператору ifабо &&т.д. без дужок. Для складних порівнянь використовуйте дужки з відповідними операторами.

І так, я знаю , що немає такого поняття , як рідний логічного типу в Bash, і що ifі [та trueтехнічно «команда» , а не «заява»; це лише дуже основне, функціональне пояснення.


1
FYI, якщо ви хочете використовувати 1/0 як True / False у своєму коді, це if (( $x )); then echo "Hello"; fiвідображає повідомлення для, x=1але не для x=0або x=(невизначено).
Джонатан Н

3

Я виявив, що можу виконувати якусь основну логіку, виконуючи щось на кшталт:

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

6
Один може , але це дійсно погана ідея. ( $A && $B )це напрочуд неефективна операція - ви нерестуєте підпроцес за допомогою виклику fork(), потім розділяєте рядок на вміст, $Aщоб генерувати список елементів (у цьому випадку розміру один) та оцінюючи кожен елемент як глобус (у цьому випадку один що оцінює для себе), а потім запускає результат у вигляді команди (у цьому випадку вбудована команда).
Чарльз Даффі

3
Більше того, якщо ваш trueі falseрядки надходять з іншого місця, існує ризик, що вони насправді можуть містити вміст, відмінний від trueабо false, і ви можете отримати несподівану поведінку аж до довільного виконання команд.
Чарльз Даффі

6
Це багато, набагато безпечніше писати A=1; B=1і if (( A && B ))- розглядати їх як числові - або [[ $A && $B ]], який обробляє порожній рядок , як помилкове і непорожній рядок , як вірно.
Чарльз Даффі

3
(зауважте, що я використовую лише імена змінних all-caps лише вище, щоб дотримуватись конвенцій, встановлених відповіддю. POSIX фактично чітко визначає назви all-caps, які будуть використовуватися для змінних оточуючих середовищ та вбудованих оболонок, і резервує малі імена для використання додатків; щоб уникнути конфліктів з майбутніми середовищами ОС, особливо зважаючи на те, що встановлення змінної оболонки замінює будь-яку змінну оточуючого середовища, як-от імені, завжди ідеально шанувати цю конвенцію у власних сценаріях).
Чарльз Даффі

Дякую за розуміння!
Родріго

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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.