เหตุใดจึงตรวจสอบว่ามีโฟลเดอร์อยู่โดยใช้ -d ในสตริงว่างเปล่าส่งคืนจริงหรือไม่


8

ฉันกำลังเขียนสคริปต์และเขียนสิ่งที่ชอบ

ARTIFACTS="/SOME/PATH"
[ -d $ARTIFCATS ] && rm -rf $ARTIFACTS/*

สิ่งที่เกิดขึ้นคือความโง่เขลาของฉันที่ฉันได้ดำเนินการบรรทัดที่สองโดยไม่ต้องดำเนินการครั้งแรก มันกลับกลายเป็นว่า [-d ""] คืนค่าจริงและนิพจน์กลายเป็น

rm -rf /*

โชคดีที่มันเป็นเพียงเครื่องทดสอบและฉันไม่ใช่ sudo แต่ถึงแม้ว่าฉันทำข้อมูลหาย

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

ฉันแก้ไขปัญหาโดยใช้

[ -e $ARTIFACTS ]
ซึ่งดูเหมือนว่าจะทำงาน

ไชโย


5
หรือบางทีคุณรันทั้งสองบรรทัด ในตัวอย่างโค้ดด้านบนคุณไม่เคยตั้ง ARTIFCATS
Buhb

2
ฉันก็จะเขียนว่าเป็นโดยไม่ต้องrm -rf $ARTIFACTS /*นี่จะเป็นการลบ$ARTIFACTSไดเรกทอรีซึ่งก็ดีเพราะถ้าฉันต้องการให้แน่ใจว่ามันมีอยู่ก่อนที่จะใส่อะไรลงไปฉันจะดำเนินการmkdir -p $ARTIFACTSต่อไป มันจะลบไฟล์ที่ซ่อนอยู่ข้างใน$ARTIFACTSซึ่งก็ใช้ได้เพราะฉันจะไม่เขียนrm -rf $ARTIFACTS/*ถ้า$ARTIFACTSมีอะไรที่ฉันต้องการบันทึก
Christoffer Hammarström

@ ChristofferHammarströmจริงมาก
Moataz Elmasry

คำตอบ:


9

1. การทดสอบทั้งสองนี้คืนค่าจริง :

# [ -d ] && echo true || echo false
true
# [ -d $SOME_UNSET_VAR ] && echo true || echo false
true

ตาม POSIX (ตามที่อธิบายโดย @Tim)

2. แต่สิ่งนี้จะคืนค่าเป็นเท็จ ( ไม่ใช่ ความจริงตามที่ระบุไว้ในคำถาม)

# [ -d "" ] && echo true || echo false
false

เพราะtestเรียกว่ามีสองข้อโต้แย้ง (แม้ว่าข้อที่สองจะเป็นสตริงว่าง)

3. นั่นเป็นเหตุผลว่าทำไมจึงควรใช้[[ … ]]แทนtest( [ … ]) ซึ่งกระสุนปัจจุบันส่วนใหญ่ (ทั้งหมด?) ให้ โครงสร้างนี้ตรวจสอบว่าคุณให้ข้อโต้แย้งเพียงพอหรือไม่หากเกิดข้อผิดพลาดและยกเลิก

# [[ -d ]] && echo true || echo false
bash: unexpected argument `]]' to conditional unary operator
bash: syntax error near `]]'

หรือทำตัวเหมือนที่คาดไว้:

# [[ -d $SOME_UNSET_VAR ]] && echo true || echo false
false

4. และตามที่ @Gilles ชี้ให้เห็นว่าสิ่งที่สำคัญยิ่งกว่าคือการแทนที่การเสนอราคาซ้ำ ดังนั้น-d "$SOME_UNSET_VAR"ขยายไป-d ""และกลับเท็จแม้ด้วยtest(เท่ากับกรณีที่ 2) ดังนั้นสิ่งนี้ยังเข้ากันได้กับเชลล์เป้าหมายsh:

# [ -d "$SOME_UNSET_VAR" ] && echo true || echo false
false

ทดสอบด้วย bash 3.00.16 (1) และ 4.1.5 (1)


1
ทุบตี ksh และ zsh ให้แต่ไม่ธรรมดา[[ … ]] shการปฏิบัติที่สำคัญจริงๆคือการใส่เครื่องหมายอัญประกาศคู่ล้อมรอบการแทนที่คำสั่ง : [ -d "$ARTIFACTS" ].
Gilles 'หยุดความชั่วร้าย'

6

คำถามนี้ได้รับการตอบแล้วใน StackOverflow มันบอกว่าเป็นไปตามมาตรฐาน POSIX testควรกลับมาประสบความสำเร็จถ้ามันถูกเรียกว่ามีข้อโต้แย้งที่ไม่ว่างเปล่าหนึ่ง (และไม่มีข้อโต้แย้งอื่น ๆ )

นี่ควรเป็นกรณีที่มีtest -e(และอันที่จริงมันเป็นในระบบของฉัน) ดังนั้นควรระมัดระวัง

ใช้แทน:

[ -d "$ARTIFACTS" ]

test จะถูกเรียกด้วยสองอาร์กิวเมนต์แม้ว่าตัวแปรจะว่างเปล่าและส่งคืนค่าเท็จในกรณีนี้


"อาร์กิวเมนต์ที่ไม่มีข้อยกเว้นหนึ่งข้อ" - นี่เป็นข้อมูลสรุปที่ทำให้เข้าใจผิด - หน้าเว็บที่คุณเชื่อมโยงการกล่าวถึงนั้นถูกเรียกด้วยอาร์กิวเมนต์หนึ่งตัวและอาร์กิวเมนต์นั้นไม่ว่างเปล่า ไม่มีอะไรเกี่ยวกับกรณีของอาร์กิวเมนต์ที่ไม่มีข้อยกเว้นตามมาด้วยอาร์กิวเมนต์ที่ว่างเปล่า ทางออกที่ถูกต้องควรล้อมรอบชื่อตัวแปรในเครื่องหมายคำพูด
Random832

@ Random832 ขอบคุณ! ฉันใช้การเปลี่ยนแปลงที่คุณแนะนำทั้งสองอย่าง
ทิม

[ ! -z $ARTIFACTS ] && [ -d $ARTIFACTS ]ไม่ดี: มันเหมาะสำหรับกรณีโดยเฉพาะอย่างยิ่งเมื่อ$ARTIFACTSเป็นที่ว่างเปล่า แต่ล้มเหลวเมื่ออาจจะมีช่องว่างหรือ$ARTIFACTS เป็นวิธีที่ถูกต้องในการทำ (หรือในเชลล์ที่มี) \[?*[ -d "$ARTIFACTS" ][[ -d $ARTIFACTS ]][[ … ]]
Gilles 'หยุดความชั่วร้าย'

@Gilles คุณถูกต้องฉันลบออกขอขอบคุณ!
ทิม

4

ฉันแก้ไขปัญหาโดยใช้ [-e $ ARTIFACTS] ซึ่งใช้งานได้

คุณอยู่ที่ไม่ถูกต้อง มันได้ผลเพราะ$ARTIFACTSตอนนี้ตั้งค่าเป็นบางอย่างแล้ว

เมื่อตัวแปรไม่ถูกตั้งค่าให้พูด

[ -d $SOMEVAR ]

หรือ

[ -e $SOMEVAR ]

ทั้งสองจะประเมินtrueเพราะมันหมายถึงการพูด

[ -d ]

และ

[ -e ]

ตามลำดับ (การบอกว่า[ foobar ]จะประเมินเป็นจริงเสมอ)

คำพูด

set -u

มีประโยชน์ในสถานการณ์ดังกล่าว help setจะบอกคุณ:

  -u  Treat unset variables as an error when substituting.

[-e ""] && echo "PRINT SOMETHING" ไม่ได้พิมพ์อะไรดังนั้นสิ่งนี้จะผิดได้อย่างไร
Moataz Elmasry

2
อึอึ [-e $ ARTIFACTS] ด้วยสิ่งประดิษฐ์ที่ว่างเปล่าให้ผล [-e] ไม่ใช่ [-e ""], เข้าใจแล้ว
Moataz Elmasry

4

ดูว่าคุณตั้งค่าตัวแปร ARTIFACTS และคุณกำลังตรวจสอบ ARTIFCATS อาจพิมพ์ผิดหรือเปล่า?

อย่างไรก็ตาม -d และ -e จะให้ผลลัพธ์เดียวกันกับตัวแปรที่ไม่ได้ตั้งค่า

ดังนั้นให้ใช้เครื่องหมายคำพูดคู่และจะช่วยให้คุณ

ARTIFACTS="/SOME/PATH"
[ -d "$ARTIFACTS" ] && rm -rf -- "$ARTIFACTS/"*

หมายเหตุ: หาก "/ SOME / PATH" ของคุณมีโฟลเดอร์ใด ๆ ที่มีช่องว่างสคริปต์ที่คุณกล่าวถึงจะแตกกับข้อผิดพลาด "ตัวดำเนินการไบนารีที่คาดหมาย"

ตัวอย่าง:

ARTIFACTS="/home backup/"

1) [ -d $ARTIFACTS ] && rm -rf $ARTIFACTS/*
bash: [: /home: binary operator expected

2) [ -d "$ARTIFACTS" ] && rm -rf "$ARTIFACTS"/*

จะทำดี อย่าลืมใส่เครื่องหมายคำพูดในการrmเรียกเช่นกัน ( rm -rf $ARTIFACTSจะลบอย่างร่าเริง/homeแล้วบ่นว่าbackup/*ไม่มีอยู่)

รวมถึงการ-Lตรวจสอบจะทำให้แน่ใจว่ามันเป็นไดเรกทอรีและไม่ใช่แค่ลิงค์สัญลักษณ์ไปยังไดเรกทอรี

ดังนั้นโดยทั่วไปแล้ว

[ -d "$ARTIFACTS" && ! -L "$ARTIFACTS" ] && rm -rf -- "$ARTIFACTS"/*
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.