วิธีการตรวจสอบว่าตัวแปร bash ว่างเปล่า?


765

เป็นวิธีที่ดีที่สุดในการตรวจสอบว่าตัวแปรใน bash ว่างเปล่า ("") คืออะไร?

ฉันเคยได้ยินว่าขอแนะนำให้ฉันทำ if [ "x$variable" = "x" ]

นั่นเป็นวิธีที่ถูกต้องหรือไม่ (ต้องมีสิ่งที่ตรงไปตรงมามากขึ้น)


คำตอบ:


1053

สิ่งนี้จะคืนค่าจริงหากตัวแปรไม่ได้ถูกตั้งค่าหรือตั้งค่าเป็นสตริงว่าง ("")

if [ -z "$VAR" ];

14
นั่นรวมถึงถ้าตัวแปร IS ตั้งค่าเป็น "" หรือไม่?
Brent

11
ใช่การทดสอบ ... "-z" สำหรับสตริงที่มีความยาวเป็นศูนย์
David Z

91
if [ ! -z "$VAR" ];
Aaron Copley

263
ค่าผกผันของ-zคือ-n if [ -n "$VAR" ];
เฟลิเป้อัลวาเรซ

19
เครื่องหมายคำพูดคู่รับประกันว่าตัวแปรจะไม่แยก ง่าย ๆ$varในบรรทัดคำสั่งจะถูกแยกโดยช่องว่างในรายการของพารามิเตอร์ในขณะที่"$var"จะเป็นเพียงหนึ่งพารามิเตอร์ การอ้างอิงตัวแปรบ่อยครั้งเป็นวิธีปฏิบัติที่ดีและห้ามไม่ให้คุณสะดุดชื่อไฟล์ที่มีช่องว่าง (เหนือสิ่งอื่นใด) ตัวอย่าง: หลังจากทำa="x --help"แล้วลองcat $a- มันจะให้หน้าช่วยเหลือสำหรับcatคุณ แล้วลองcat "$a"- มันจะ (ปกติ) cat: x --help: No such file or directoryกล่าวว่า พูดสั้น ๆ ให้พูดเร็วและพูดบ่อย ๆ และคุณแทบจะไม่เสียใจเลย
คะแนน _ ต่ำกว่า

247

ใน Bash เมื่อคุณไม่กังวลเกี่ยวกับความสามารถในการพกพาของเชลล์ที่ไม่สนับสนุนคุณควรใช้ไวยากรณ์วงเล็บคู่:

ข้อใดข้อหนึ่งต่อไปนี้:

if [[ -z $variable ]]
if [[ -z "$variable" ]]
if [[ ! $variable ]]
if [[ ! "$variable" ]]

ใน Bash โดยใช้เครื่องหมายวงเล็บสองช่องราคาไม่จำเป็น คุณสามารถลดความซับซ้อนของการทดสอบสำหรับตัวแปรที่จะมีค่าไปที่:

if [[ $variable ]]

ไวยากรณ์นี้เข้ากันได้กับ ksh (อย่างน้อย ksh93 อยู่ดี) มันไม่ทำงานใน POSIX บริสุทธิ์หรือเชลล์ Bourne รุ่นเก่าเช่น sh หรือ dash

ดูคำตอบของฉันที่นี่และBashFAQ / 031สำหรับข้อมูลเพิ่มเติมเกี่ยวกับความแตกต่างระหว่างวงเล็บเหลี่ยมสองชั้นและเดี่ยว

คุณสามารถทดสอบเพื่อดูว่าตัวแปรไม่มีการตั้งค่าเฉพาะ (แตกต่างจากสตริงว่าง):

if [[ -z ${variable+x} ]]

โดยที่ "x" เป็นกฎเกณฑ์

หากคุณต้องการทราบว่าตัวแปรเป็นโมฆะหรือไม่ไม่ได้ตั้งค่า:

if [[ -z $variable && ${variable+x} ]]

1
มันใช้if [[ $variable ]]งานได้ดีสำหรับฉันและไม่ต้องการแม้แต่สิ่งเดียวset -uที่ต้องการโดยหนึ่งในโซลูชันที่เสนออื่น ๆ
Teemu Leisti

3
ฉันคิดว่านี่ดีกว่าคำตอบที่ยอมรับ
qed

2
เหตุใดคุณจึงแนะนำคุณสมบัติที่ไม่สามารถพกพาได้เมื่อทำเช่นนั้นจะไม่ได้ประโยชน์
Alastair Irvine

19
@ AlastairIrvine: ฉันพูดถึงการพกพาในประโยคแรกของคำตอบของฉันชื่อคำถามและเนื้อหามีคำว่า "Bash" และคำถามนั้นติดแท็กbashและโครงสร้างวงเล็บคู่ให้ข้อดีที่ชัดเจนในหลาย ๆ ด้าน และฉันไม่แนะนำให้ผสมสไตล์วงเล็บเหลี่ยมด้วยเหตุผลเรื่องความมั่นคงและการบำรุงรักษา หากคุณต้องการความสะดวกในการพกพาตัวหารทั่วไปที่ต่ำที่สุดให้ใช้shแทน Bash หากคุณต้องการความสามารถที่เพิ่มขึ้นที่มีให้ใช้ Bash และใช้อย่างเต็มที่
Dennis Williamson

2
@BrunoBronosky: ฉันยกเลิกการแก้ไขอีกครั้ง ไม่มีข้อกำหนดสำหรับ;ในตอนท้าย thenสามารถจะอยู่ในบรรทัดถัดไปโดยไม่ต้องอัฒภาคที่ทั้งหมด
Dennis Williamson

230

ตัวแปรใน bash (และเชลล์ที่เข้ากันได้กับ POSIX) สามารถเป็นหนึ่งในสามสถานะ:

  • ไม่มีการตั้งค่า
  • ตั้งค่าเป็นสตริงว่าง
  • ตั้งเป็นสตริงที่ไม่ว่างเปล่า

เวลาส่วนใหญ่คุณเพียงแค่ต้องรู้ว่าตัวแปรถูกตั้งค่าเป็นสตริงที่ไม่ว่างเปล่าหรือไม่ แต่ในบางครั้งการแยกความแตกต่างระหว่างการตั้งค่าและการตั้งค่าเป็นสตริงว่างนั้นเป็นสิ่งสำคัญ

ต่อไปนี้เป็นตัวอย่างของวิธีที่คุณสามารถทดสอบความเป็นไปได้ที่หลากหลายและใช้งานได้ใน bash หรือเชลล์ที่รองรับ POSIX:

if [ -z "${VAR}" ]; then
    echo "VAR is unset or set to the empty string"
fi
if [ -z "${VAR+set}" ]; then
    echo "VAR is unset"
fi
if [ -z "${VAR-unset}" ]; then
    echo "VAR is set to the empty string"
fi
if [ -n "${VAR}" ]; then
    echo "VAR is set to a non-empty string"
fi
if [ -n "${VAR+set}" ]; then
    echo "VAR is set, possibly to the empty string"
fi
if [ -n "${VAR-unset}" ]; then
    echo "VAR is either unset or set to a non-empty string"
fi

นี่คือสิ่งเดียวกัน แต่ในรูปแบบตารางที่มีประโยชน์:

                        +-------+-------+-----------+
                VAR is: | unset | empty | non-empty |
+-----------------------+-------+-------+-----------+
| [ -z "${VAR}" ]       | true  | true  | false     |
| [ -z "${VAR+set}" ]   | true  | false | false     |
| [ -z "${VAR-unset}" ] | false | true  | false     |
| [ -n "${VAR}" ]       | false | false | true      |
| [ -n "${VAR+set}" ]   | false | true  | true      |
| [ -n "${VAR-unset}" ] | true  | false | true      |
+-----------------------+-------+-------+-----------+

${VAR+foo}สร้างขยายสตริงที่ว่างเปล่าถ้าVARไม่มีการตั้งค่าหรือfooถ้าVARมีการตั้งค่าเพื่ออะไร (รวมทั้งสตริงว่างเปล่า)

${VAR-foo}สร้างขยายมูลค่าของVARถ้าตั้ง (รวมถึงการตั้งค่าเป็นสตริงว่างเปล่า) และfooถ้าไม่มีการตั้งค่า สิ่งนี้มีประโยชน์สำหรับการจัดเตรียมค่าเริ่มต้นที่ผู้ใช้สามารถข้ามได้ (เช่น${COLOR-red}บอกว่าจะใช้redยกเว้นว่าตัวแปรCOLORถูกตั้งค่าเป็นบางอย่าง)

เหตุผลที่[ x"${VAR}" = x ]มักแนะนำให้ทำการทดสอบว่าตัวแปรไม่ได้ถูกตั้งค่าหรือตั้งค่าเป็นสตริงว่างเปล่านั้นเป็นเพราะการใช้งานบางส่วนของ[คำสั่ง (หรือที่รู้จักกันในชื่อtest) ว่าเป็นรถ หากVARตั้งค่าเป็นบางสิ่ง-nบางอย่างการใช้งานบางอย่างจะทำสิ่งผิดเมื่อได้รับ[ "${VAR}" = "" ]เนื่องจากอาร์กิวเมนต์แรกที่[ตีความผิดพลาดเป็น-nโอเปอเรเตอร์ไม่ใช่สตริง


3
[ -z "${VAR-set}" ]การทดสอบสำหรับการตั้งค่าตัวแปรสตริงที่ว่างเปล่ายังสามารถทำได้โดยใช้
nwellnhof

@nwellnhof: ขอบคุณ! ฉันอัพเดตคำตอบของฉันเพื่อใช้ไวยากรณ์ briefer
Richard Hansen

อะไรคือความแตกต่างระหว่างการสร้างครั้งแรกและครั้งสุดท้าย ทั้งคู่สอดคล้องกับ "VAR ไม่ได้ตั้งค่าหรือตั้งค่าเป็นสตริงที่ไม่ว่าง"
Faheem Mitha

1
@FaheemMitha: ไม่ใช่ความผิดของคุณ - คำตอบของฉันอ่านยาก ฉันเพิ่มตารางเพื่อหวังคำตอบที่ชัดเจนยิ่งขึ้น
Richard Hansen

2
การตรวจสอบที่ไม่ได้ตั้งค่านั้นไม่น่าเชื่อถือ หากผู้ใช้เรียกset -uหรือset -o nounsetทุบตีการทดสอบจะส่งผลให้เกิดข้อผิดพลาด "bash: VAR: unbound variable" ดูstackoverflow.com/a/13864829สำหรับการตรวจสอบที่เชื่อถือได้ [ -z "${VAR:-}" ]ฉันไปเพื่อตรวจสอบว่าตัวแปรเป็นโมฆะหรือไม่มีการตั้งค่าเป็น ฉันตรวจสอบว่าตัวแปรไม่ว่างเปล่า[ "${VAR:-}" ]หรือไม่
เควินจิน

38

-z เป็นวิธีที่ดีที่สุด

ตัวเลือกอื่นที่ฉันใช้คือตั้งค่าตัวแปร แต่มันสามารถแทนที่ได้โดยตัวแปรอื่นเช่น

export PORT=${MY_PORT:-5432}

หาก$MY_PORTตัวแปรว่างเปล่าPORTจะได้รับการตั้งค่าเป็น 5432 มิฉะนั้น PORT จะถูกตั้งค่าMY_PORTเป็น หมายเหตุไวยากรณ์รวมถึงเครื่องหมายจุดคู่และเส้นประ


พบสิ่งนี้โดยบังเอิญในวันนี้และมันเป็นสิ่งที่ฉันต้องการ ขอบคุณ! ฉันต้องทนset -o nounsetในบางสคริปต์
opello

24

หากคุณสนใจที่จะแยกความแตกต่างของกรณีของ set-empty กับสถานะ unset ให้ดูที่ตัวเลือก -u สำหรับ bash:

$ set -u
$ echo $BAR
bash: BAR: unbound variable
$ [ -z "$BAR" ] && echo true
bash: BAR: unbound variable
$ BAR=""
$ echo $BAR

$ [ -z "$BAR" ] && echo true
true

8

ทางเลือกที่ฉันเคยเห็น[ -z "$foo" ]มีดังต่อไปนี้ แต่ฉันไม่แน่ใจว่าทำไมผู้คนถึงใช้วิธีนี้ทุกคนรู้

[ "x${foo}" = "x" ]

อย่างไรก็ตามหากคุณไม่อนุญาตให้ใช้ตัวแปรที่ไม่ได้ตั้งค่า (ไม่ว่าจะโดยset -uหรือset -o nounset) คุณจะประสบปัญหากับทั้งสองวิธี มีวิธีแก้ไขง่ายๆดังนี้:

[ -z "${foo:-}" ]

หมายเหตุ: สิ่งนี้จะปล่อยให้ตัวแปรยกเลิกการตั้งค่า


3
มีความคิดเห็นเกี่ยวกับทางเลือกที่เป็น-zที่pubs.opengroup.org/onlinepubs/009695399/utilities/test.html -zโดยทั่วไปก็ไม่ได้หมายความว่าจะเป็นทางเลือกที่จะ แต่มันจัดการกรณีที่$fooสามารถขยายไปยังสิ่งที่เริ่มต้นด้วย metacharacter ที่[หรือtestจะสับสนโดย การใส่ตัวอักษรที่ไม่ใช่ตัวอักษรตามอำเภอใจในตอนต้นจะช่วยลดโอกาสที่จะเกิดขึ้น
James Sneeringer

6

คำถามถามวิธีการตรวจสอบว่าตัวแปรเป็นสตริงที่ว่างเปล่าและคำตอบที่ดีที่สุดที่จะได้รับอยู่แล้วว่า
แต่ฉันลงจอดที่นี่หลังจากช่วงเวลาหนึ่งผ่านการเขียนโปรแกรมใน php และสิ่งที่ฉันค้นหาจริง ๆ คือการตรวจสอบว่าฟังก์ชั่นที่ว่างเปล่าใน phpทำงานในเชลล์ทุบตี
หลังจากอ่านคำตอบฉันรู้ว่าฉันไม่ได้คิดอย่างถูกต้องในการทุบตี แต่อย่างใดในเวลานั้นฟังก์ชั่นที่ว่างเปล่าใน php จะมีประโยชน์มากในรหัสทุบตีของฉัน
เนื่องจากฉันคิดว่าสิ่งนี้สามารถเกิดขึ้นกับผู้อื่นได้ฉันตัดสินใจที่จะแปลงฟังก์ชัน php empty เป็น bash

ตามคู่มือ php :
ตัวแปรถูกพิจารณาว่าว่างเปล่าหากไม่มีอยู่หรือถ้าค่าเป็นหนึ่งในค่าต่อไปนี้:

  • "" (สตริงว่าง)
  • 0 (0 เป็นจำนวนเต็ม)
  • 0.0 (0 เป็นแบบลอย)
  • "0" (0 เป็นสตริง)
  • อาร์เรย์ที่ว่างเปล่า
  • ตัวแปรที่ประกาศ แต่ไม่มีค่า

แน่นอนกรณีที่เป็นโมฆะและเท็จไม่สามารถแปลงเป็น bash ดังนั้นพวกเขาจะถูกละเว้น

function empty
{
    local var="$1"

    # Return true if:
    # 1.    var is a null string ("" as empty string)
    # 2.    a non set variable is passed
    # 3.    a declared variable or array but without a value is passed
    # 4.    an empty array is passed
    if test -z "$var"
    then
        [[ $( echo "1" ) ]]
        return

    # Return true if var is zero (0 as an integer or "0" as a string)
    elif [ "$var" == 0 2> /dev/null ]
    then
        [[ $( echo "1" ) ]]
        return

    # Return true if var is 0.0 (0 as a float)
    elif [ "$var" == 0.0 2> /dev/null ]
    then
        [[ $( echo "1" ) ]]
        return
    fi

    [[ $( echo "" ) ]]
}



ตัวอย่างการใช้งาน:

if empty "${var}"
    then
        echo "empty"
    else
        echo "not empty"
fi



การสาธิต:
ตัวอย่างต่อไปนี้:

#!/bin/bash

vars=(
    ""
    0
    0.0
    "0"
    1
    "string"
    " "
)

for (( i=0; i<${#vars[@]}; i++ ))
do
    var="${vars[$i]}"

    if empty "${var}"
        then
            what="empty"
        else
            what="not empty"
    fi
    echo "VAR \"$var\" is $what"
done

exit

เอาท์พุท:

VAR "" is empty
VAR "0" is empty
VAR "0.0" is empty
VAR "0" is empty
VAR "1" is not empty
VAR "string" is not empty
VAR " " is not empty

ต้องบอกว่าในตรรกะทุบตีการตรวจสอบในศูนย์ในฟังก์ชั่นนี้อาจทำให้เกิดปัญหาข้างเคียงทุกคนที่ใช้ฟังก์ชั่นนี้ควรประเมินความเสี่ยงนี้และอาจตัดสินใจที่จะตัดเช็คเหล่านั้นออกจากคนแรกเท่านั้น


ข้างในฟังก์ชั่นempty- ทำไมคุณถึงเขียน[[ $( echo "1" ) ]] ; returnแทนที่จะเรียบง่ายreturn 1?
Dor

5

if-then และ -z ทั้งหมดนั้นไม่จำเป็น

["$ foo"] && echo "foo ไม่ว่าง"
["$ foo"] || echo "foo is empty"

3
ซึ่งจะล้มเหลวหาก foo มีช่องว่างเท่านั้น
Brian

2
จะล้มเหลวในเชลล์บางตัวถ้าfooเริ่มต้นด้วยเครื่องหมายขีดเนื่องจากตีความว่าเป็นตัวเลือก เช่นบน Solaris ksh , zshและทุบตีนี้ไม่มีปัญหาการดวลจุดโทษและ/ bin / การทดสอบจะล้มเหลว
KTF

4

สิ่งนี้เป็นจริงทุกอย่างเมื่อตั้งค่า $ FOO และไม่มีค่า:

[ "${FOO+x}" = x ] && [ -z "$FOO" ]

4

ส่วนตัวชอบวิธีที่ชัดเจนมากขึ้นในการตรวจสอบ:

if [ "${VARIABLE}" == "" ]; then
  echo VARIABLE is empty
else
  echo VARIABLE is not empty
fi

1
เชลล์บางตัวไม่ยอมรับเครื่องหมายเท่ากับสองเท่า
Dennis Williamson

1
คำถามคือเกี่ยวกับทุบตี ฉันใช้ทุบตี ทำงานได้ดีสำหรับฉัน คุณกำลังพูดเรื่องอะไรกันแน่?
Fedir RYKHTIK

2
หากคุณกำลังใช้ Bash คุณควรใช้วงเล็บเหลี่ยมสองชั้น ความคิดเห็นก่อนหน้าของฉันคือคำแถลงง่ายๆสำหรับผู้ที่อาจอ่านคำตอบของคุณและใช้เชลล์ที่แตกต่างกัน
Dennis Williamson

วงเล็บเหลี่ยมเดี่ยวไม่เป็นไรในกรณีนี้โปรดดูserverfault.com/questions/52034/ ​​…
Luca Borrione


4

5 เซนต์ของฉัน: นอกจากนี้ยังมีไวยากรณ์ที่สั้นกว่าif ...อันนี้:

VALUE="${1?"Usage: $0 value"}"

บรรทัดนี้จะตั้งค่า VALUE หากมีการระบุอาร์กิวเมนต์และจะพิมพ์ข้อความแสดงข้อผิดพลาดที่เตรียมไว้พร้อมกับหมายเลขบรรทัดสคริปต์ในกรณีที่เกิดข้อผิดพลาด (และจะยุติการเรียกใช้สคริปต์)

อีกตัวอย่างหนึ่งสามารถพบได้ในabs-guide (ค้นหา«ตัวอย่างที่ 10-7 »)


0

ไม่ใช่คำตอบที่แน่นอน แต่พบกับเคล็ดลับนี้ หากสตริงที่คุณต้องการมาจาก "คำสั่ง" คุณสามารถจัดเก็บคำสั่งใน env ได้ ตัวแปรแล้วดำเนินการทุกครั้งสำหรับคำสั่ง if แล้วไม่จำเป็นต้องใช้วงเล็บ!

ตัวอย่างเช่นคำสั่งนี้ซึ่งกำหนดว่าคุณเป็นเดเบียนหรือไม่:

grep debian /proc/version

ตัวอย่างเต็มรูปแบบ:

IS_DEBIAN="grep -i debian /proc/version"

if $IS_DEBIAN; then
  echo 'yes debian'
else
  echo 'non debian'
fi

ดังนั้นนี่เป็นวิธีการทางอ้อม (เรียกใช้ซ้ำทุกครั้ง) เพื่อตรวจสอบสตริงว่างเปล่า (มันเกิดขึ้นเพื่อตรวจสอบการตอบสนองข้อผิดพลาดจากคำสั่ง

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.