ใช้ unset เทียบกับการตั้งค่าตัวแปรให้ว่างเปล่า


108

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

ตัวอย่าง:

string_equals() {
    if [[ ! $1 = $2 ]]; then
            error_message="Expected '$1' to be '$2'."

            return 1
    fi
}

ดังนั้นเมื่อใช้ตัวจับคู่และล้มเหลวระบบจะตั้งค่า error_message เท่านั้น

ในเวลาต่อมาฉันทดสอบว่าการทดสอบสำเร็จหรือไม่ หากสำเร็จฉันจะพิมพ์ความคาดหวังเป็นสีเขียวหากล้มเหลวเป็นสีแดง

นอกจากนี้อาจมีชุด error_message ดังนั้นฉันจึงทดสอบว่ามีข้อความอยู่หรือไม่พิมพ์แล้วจึงยกเลิกการตั้งค่า (เนื่องจากการทดสอบต่อไปนี้อาจไม่ได้ตั้งค่า a error_message):

if [[ $error_message ]]; then
    printf '%s\n' "$error_message"

    unset -v error_message
fi

ตอนนี้คำถามของฉันคือถ้าจะดีกว่าที่จะยกเลิกการตั้งค่าตัวแปรหรือตั้งค่าเป็น '' เช่น

error_message=''

อันไหนดีกว่า? มันสร้างความแตกต่างได้จริงหรือ? หรือฉันควรตั้งค่าสถานะเพิ่มเติมเพื่อระบุว่ามีการตั้งค่าข้อความ


1
ถ้าคุณไม่เคยเปรียบเทียบerror_messageกับสิ่งอื่นฉันว่ามันไม่สำคัญ อย่างไรก็ตามฉันคิดว่าคุณต้องการ[[ $error_message ]]ไม่เช่นนั้นคุณกำลังทดสอบว่ามีสตริงตัวอักษร "error_message" อยู่
chepner

@chepner ใช่เป็นการพิมพ์ผิด ซ่อมมัน.
วิธีช่วยเหลือ

คำตอบ:


143

ส่วนใหญ่คุณจะไม่เห็นความแตกต่างเว้นแต่คุณจะใช้set -u:

/home/user1> var=""
/home/user1> echo $var

/home/user1> set -u
/home/user1> echo $var

/home/user1> unset var
/home/user1> echo $var
-bash: var: unbound variable

จริงๆแล้วมันขึ้นอยู่กับว่าคุณจะทดสอบตัวแปรอย่างไร

ฉันจะเพิ่มว่าวิธีการทดสอบที่ฉันต้องการคือ:

[[ -n $var ]]  # True if the length of $var is non-zero

หรือ

[[ -z $var ]]  # True if zero length

43
var=ไม่ "ไม่ได้ตั้งค่า" มันเป็นเพียงสตริงว่างที่ไม่มีเครื่องหมายคำพูด นอกจากนี้การset -uขยายพารามิเตอร์ในรูปแบบต่างๆของ bash ยังสามารถแยกความแตกต่างระหว่างค่าที่ไม่ได้ตั้งค่าและค่าว่าง: ${foo:bar}ขยายเป็น "bar" เมื่อfooไม่ได้ตั้งค่า แต่ "" เมื่อใดfooเป็นค่าว่างในขณะที่${foo:-bar}ขยายเป็น "bar" หากไม่ได้ตั้งค่าหรือค่าว่าง
chepner

1
[[ -n $var ]]เป็นเท็จหากvarตั้งค่าเป็นสตริงว่าง
chepner

1
หากคุณใช้ 'ประกาศ -p' เพื่อตรวจสอบตัวแปร 'มีอยู่' จากนั้น var = จะแสดง 'ประกาศ - var = ""' คุณต้องใช้ unset เพื่อกำจัดมันแน่นอนว่าหากเป็นตัวแปรแบบอ่านอย่างเดียวคุณจะไม่สามารถรับได้ กำจัดมันแน่นอน นอกจากนี้หากคุณ var = บางอย่างภายในฟังก์ชันคุณจะไม่ต้องกังวลเกี่ยวกับการกำจัดมันหากคุณใช้ "local var = value" โปรดระวังเนื่องจาก "ประกาศ var = value" เป็นแบบโลคัลเช่นกันในกรณีของการประกาศ คุณต้องตั้งค่าส่วนกลางอย่างชัดเจนโดยใช้ "ประกาศ -g var = ค่า" โดยไม่ต้องประกาศว่าคุณต้องตั้งค่าให้เป็นแบบโลคัลอย่างชัดเจนโดยใช้ "local" โดยจะมีการลบ vars แบบโกลบอล / w unset เท่านั้น
osirisgothra

@osirisgothra: ประเด็นที่ดีเกี่ยวกับตัวแปรท้องถิ่น โดยทั่วไปแล้วควรประกาศ (หรือแปล) ตัวแปรทั้งหมดในฟังก์ชันเพื่อให้มีการห่อหุ้ม
cdarke

3
@chepner ควรจะแทน${foo-bar} ${foo:bar}ทดสอบด้วยตัวคุณเอง:unset a; echo ">${a:-foo}-${a:foo}-${a-foo}<"
Liviu Chircu

17

ดังที่ได้กล่าวไปแล้วการใช้ unset นั้นแตกต่างกับอาร์เรย์เช่นกัน

$ foo=(4 5 6)

$ foo[2]=

$ echo ${#foo[*]}
3

$ unset foo[2]

$ echo ${#foo[*]}
2

2

ดังนั้นโดยการยกเลิกการตั้งค่าดัชนีอาร์เรย์ 2 คุณจึงลบองค์ประกอบนั้นในอาร์เรย์เป็นหลักและลดขนาดอาร์เรย์ (?)

ทำข้อสอบเอง ..

foo=(5 6 8)
echo ${#foo[*]}
unset foo
echo ${#foo[*]}

ซึ่งส่งผลให้ ..

3
0

ดังนั้นเพื่อชี้แจงว่าการไม่ตั้งค่าอาร์เรย์ทั้งหมดในความเป็นจริงจะลบออกทั้งหมด


0

จากความคิดเห็นด้านบนนี่คือการทดสอบง่ายๆ:

isunset() { [[ "${!1}" != 'x' ]] && [[ "${!1-x}" == 'x' ]] && echo 1; }
isset()   { [ -z "$(isunset "$1")" ] && echo 1; }

ตัวอย่าง:

$ unset foo; [[ $(isunset foo) ]] && echo "It's unset" || echo "It's set"
It's unset
$ foo=     ; [[ $(isunset foo) ]] && echo "It's unset" || echo "It's set"
It's set
$ foo=bar  ; [[ $(isunset foo) ]] && echo "It's unset" || echo "It's set"
It's set
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.