ทุบตีถ้า [เท็จ]; ผลตอบแทนจริง


151

รับการเรียนรู้ทุบตีในสัปดาห์นี้และวิ่งเข้าไปในอุปสรรค์

#!/bin/sh

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

สิ่งนี้จะออกจริงทุกครั้งแม้ว่าเงื่อนไขจะดูเหมือนจะบ่งบอกเป็นอย่างอื่น หากฉันถอดขายึดออก[]แล้วก็ใช้งานได้ แต่ฉันไม่เข้าใจว่าทำไม


3
นี่คือสิ่งที่จะช่วยให้คุณเริ่มต้นได้
keyser

10
BTW สคริปต์ที่เริ่มต้นด้วย#!/bin/shไม่ใช่สคริปต์ทุบตี - เป็นสคริปต์ POSIX sh แม้ว่าล่าม POSIX sh บนระบบของคุณจะถูกจัดเตรียมโดย bash แต่จะปิดส่วนขยายจำนวนมาก หากคุณต้องการเขียนสคริปต์ทุบตีให้ใช้#!/bin/bashหรือเทียบเท่าที่เหมาะสมกับท้องถิ่นของมัน ( #!/usr/bin/env bashเพื่อใช้ล่ามทุบตีแรกในเส้นทาง)
Charles Duffy

สิ่งนี้มีผล[[ false ]]เช่นกัน
CMCDragonkai

คำตอบ:


192

คุณกำลังเรียกใช้[(aka test) คำสั่งที่มีการโต้แย้ง "เท็จ" falseไม่ได้ใช้คำสั่ง เนื่องจาก "false" เป็นสตริงที่ไม่ว่างtestคำสั่งจึงจะสำเร็จ หากต้องการเรียกใช้คำสั่งให้วาง[คำสั่ง

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

4
ความคิดที่ผิด ๆ ว่าเป็นคำสั่งหรือแม้กระทั่งสายดูเหมือนจะแปลกสำหรับฉัน แต่ฉันคิดว่ามันใช้งานได้ ขอบคุณ
tenmiles

31
bashไม่มีประเภทข้อมูลบูลีนและไม่มีคำหลักที่แสดงถึงความจริงและเท็จ ifคำสั่งเพียงตรวจสอบว่าคำสั่งที่คุณให้ประสบความสำเร็จหรือล้มเหลว testคำสั่งจะใช้เวลาการแสดงออกและประสบความสำเร็จถ้าการแสดงออกเป็นความจริง สตริงที่ไม่ว่างเปล่าคือนิพจน์ที่ประเมินว่าเป็นจริงเช่นเดียวกับในภาษาการเขียนโปรแกรมอื่น ๆ ส่วนใหญ่ falseเป็นคำสั่งที่ล้มเหลวเสมอ (โดยการเปรียบเทียบtrueเป็นคำสั่งที่ประสบความสำเร็จเสมอ)
chepner

2
if [[ false ]];ผลตอบแทนจริงเช่นกัน if [[ 0 ]];เช่นเดียวกับ และif [[ /usr/bin/false ]];ไม่ข้ามบล็อคอย่างใดอย่างหนึ่ง เท็จหรือ 0 สามารถประเมินให้ไม่ใช่ศูนย์ได้อย่างไร? ประณามสิ่งนี้น่าผิดหวัง หากมีความสำคัญ OS X 10.8; Bash 3.
jww

7
อย่าเข้าใจผิดfalseว่าค่าคงที่บูลีนหรือ0ตัวอักษรเต็มจำนวน ทั้งสองเป็นเพียงสตริงธรรมดา [[ x ]]เทียบเท่ากับ[[ -n x ]]สตริงใด ๆxที่ไม่ได้ขึ้นต้นด้วยยัติภังค์ bashไม่ได้มีการใด ๆคงที่บูลีนในบริบทใด ๆ สตริงที่มีลักษณะเหมือนจำนวนเต็มจะถือว่าเป็นภายในดังกล่าว(( ... ))หรือกับผู้ประกอบการเช่น-eqหรือภายใน-lt [[ ... ]]
chepner

2
@ tbc0 มีข้อกังวลมากกว่าอยู่ที่การอ่าน หากตัวแปรที่กำหนดโดยรหัสไม่ได้ทั้งหมดภายใต้การควบคุมของคุณ (หรือที่สามารถจัดการภายนอกไม่ว่าจะเป็นโดยชื่อไฟล์ที่ผิดปกติหรืออื่น ๆ ) if $predicateสามารถถูกทำร้ายรันรหัสศัตรูในลักษณะที่if [[ $predicate ]]ไม่สามารถ
Charles Duffy

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)

กล่าวโดยย่อหากคุณต้องการทดสอบสิ่งที่ผ่าน / ไม่ผ่าน (หรือที่เรียกว่า "จริง" / "เท็จ") ให้ส่งคำสั่งไปยังคำสั่งของคุณifหรือ&&อื่น ๆ โดยไม่ต้องใส่เครื่องหมายวงเล็บ สำหรับการเปรียบเทียบที่ซับซ้อนให้ใช้วงเล็บกับตัวดำเนินการที่เหมาะสม

และใช่ฉันรู้ว่าไม่มีสิ่งเช่น boolean ประเภทดั้งเดิมใน Bash และนั่นifและ[และtrueเป็น "คำสั่ง" ทางเทคนิคและไม่ใช่ "คำสั่ง" นี่เป็นเพียงคำอธิบายพื้นฐานที่ใช้งานได้ดีมาก


1
FYI หากคุณต้องการใช้ 1/0 เป็น True / False ในรหัสของคุณสิ่งนี้if (( $x )); then echo "Hello"; fiจะแสดงข้อความสำหรับx=1แต่ไม่ใช่สำหรับx=0หรือx=(ไม่ได้กำหนด)
Jonathan H

3

ฉันพบว่าฉันสามารถทำตรรกะพื้นฐานบางอย่างด้วยการทำสิ่งที่ชอบ:

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

6
หนึ่งสามารถแต่มันเป็นความคิดที่ดีจริงๆ ( $A && $B )เป็นการดำเนินการที่ไม่มีประสิทธิภาพอย่างน่าประหลาดใจ - คุณกำลังวางระบบย่อยผ่านการโทรfork()แล้วทำการแยกเนื้อหาของ$Aเพื่อสร้างรายการองค์ประกอบ (ในกรณีของขนาดหนึ่ง) และประเมินแต่ละองค์ประกอบเป็นแบบกลม (ในกรณีนี้ ที่ประเมินกับตัวเอง) จากนั้นรันผลลัพธ์เป็นคำสั่ง (ในกรณีนี้คือคำสั่ง builtin)
ชาร์ลส์ดัฟฟี่

3
นอกจากนี้หากคุณtrueและfalseสายอักขระมาจากที่อื่นอาจมีความเสี่ยงที่พวกเขาอาจมีเนื้อหาที่นอกเหนือจากtrueหรือfalseและคุณอาจได้รับพฤติกรรมที่ไม่คาดคิดและรวมถึงการดำเนินการคำสั่งโดยพลการ
ชาร์ลส์ดัฟฟี่

6
มันมาก, มากปลอดภัยในการเขียนA=1; B=1และif (( A && B ))- รักษาพวกเขาเป็นตัวเลข - หรือ[[ $A && $B ]]ซึ่งถือว่าเป็นสตริงว่างเป็นเท็จและสตริงไม่ว่างเปล่าเป็นจริง
Charles Duffy

3
(โปรดทราบว่าฉันใช้ชื่อตัวแปร all-caps ด้านบนเท่านั้นเพื่อทำตามอนุสัญญาที่สร้างขึ้นโดยคำตอบ - POSIX จริง ๆ แล้วระบุชื่อ all-caps อย่างชัดเจนเพื่อใช้สำหรับตัวแปรสภาพแวดล้อมและเชลล์บิวด์และสงวนชื่อตัวพิมพ์เล็กสำหรับการใช้งาน เพื่อหลีกเลี่ยงความขัดแย้งกับสภาพแวดล้อมระบบปฏิบัติการในอนาคตโดยเฉพาะอย่างยิ่งเนื่องจากการตั้งค่าตัวแปรเชลล์จะเขียนทับตัวแปรสภาพแวดล้อมใด ๆ ที่มีชื่อคล้ายกันจึงเหมาะอย่างยิ่งที่จะเคารพการประชุมในสคริปต์ของตัวเอง)
Charles Duffy

ขอบคุณสำหรับข้อมูลเชิงลึก!
Rodrigo

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.