การทดสอบหรือ [หรือ [พกพาได้มากกว่าระหว่างเชลล์ bash และระหว่างเชลล์อื่นหรือไม่?


44

ฉันเห็นฉันสามารถทำ

$ [ -w /home/durrantm ] && echo "writable"
writable

หรือ

$ test -w /home/durrantm && echo "writable"
writable

หรือ

$ [[ -w /home/durrantm ]] && echo "writable"
writable

ฉันชอบใช้ไวยากรณ์ที่สาม พวกมันเทียบเท่ากันในทุกวิถีทางและสำหรับคดีลบและคดีขอบ มีความแตกต่างในการพกพาเช่นระหว่างทุบตีบน Ubuntu และบน OS X หรือรุ่นทุบตีเก่า / ใหม่กว่าเช่นก่อน / หลัง 4.0 และพวกเขาทั้งสองขยายการแสดงออกในลักษณะเดียวกันหรือไม่


2
สำหรับ[ … ]vs [[ … ]]vs test …มีคำตอบที่สมบูรณ์มากขึ้นในคำถามที่ซ้ำกันส่วนใหญ่นี้
Gilles 'หยุดความชั่วร้าย'

สำหรับปัญหาเฉพาะของการทดสอบความสามารถในการเขียนของไฟล์ให้ดูที่วิธีทดสอบแบบไม่รุกรานสำหรับการเข้าถึงเพื่อเขียนไฟล์
G-Man กล่าวว่า 'Reinstate Monica'

1
มีการพูดว่า: "ไม่มีรหัสพกพา, รหัสเท่านั้นที่ได้รับการพอร์ต" คำแนะนำของฉันเกี่ยวกับสิ่งนี้: ใช้แบบฟอร์มที่อ่านง่ายที่สุด (อาจเป็น [[... ]]) และลองใช้กับแพลตฟอร์มทั้งหมดที่คุณต้องการให้การสนับสนุน ไม่มีการใช้งานมากเกินไปในการปิดบังสคริปต์ของคุณดังนั้นพวกเขาจึงทำงานบนแพลตฟอร์มโบราณที่คุณและผู้ชมเป้าหมายของคุณไม่ได้ใช้ มันจะทำให้โค้ดของคุณอ่านยากแนะนำข้อผิดพลาดที่ไม่จำเป็นและอาจทำให้เกิดปัญหาด้านความปลอดภัย (เช่นเดียวกับ openssl)
stefan.schwetschke

คำตอบ:


31

[คือคำพ้องความหมายของtestคำสั่งและพร้อมกันคือคำสั่งbash builtinและคำสั่งแยกต่างหาก แต่[[เป็นคำหลักทุบตีและทำงานได้ในบางรุ่นเท่านั้น ดังนั้นด้วยเหตุผลของการพกพาคุณจะดีกว่าการใช้ซิงเกิ้ล[]หรือtest

[ -w "/home/durrantm" ] && echo "writable"

2
[POSIX คืออะไรในตัวหรือ Bash ในตัว?
Sandburg

@Sandburg POSIX เป็นมาตรฐานดังนั้นจึงไม่สามารถมีบิวด์อินได้ ไม่ได้กำหนดว่าควรสร้างบางสิ่งในล่ามหรือไม่สิ่งที่ต้องทำ กฎเหล่านี้ใช้กับทั้งสองรูปแบบtestและ[เหมือนกัน หากเชลล์สามารถทำการประเมินด้วยตัวเองมันจะช่วยคุณในการประมวลผลและทำให้มันค่อนข้างเร็วขึ้น แต่ผลลัพธ์จะต้องเหมือนกัน
Bachsau

@ Bachsau ที่ยังไม่ตอบคำถามของ Sandburg ว่าพฤติกรรมของ[ถูกกำหนดโดย POSIX หรือไม่และดังนั้นจึงพกพาได้สูงสุด (ซึ่งน่าจะเป็นประเด็นทั้งหมดของคำถามนี้หากฉันไม่เข้าใจผิด)
JamesTheAwesomeDude

@Sandburg (และคนอื่นสะดุดข้ามนี้): ใช่มันดูเหมือนว่าทั้งสอง[และtest มี POSIX ดูเหมือนจะไม่มี "แนะนำ" แม้ว่าจะมีความแตกต่างเพียงอย่างเดียว (?) ที่ฉันสามารถมองเห็นได้ระหว่างพวกเขาก็คือtest"จะไม่รู้จักอาร์กิวเมนต์" - "[เป็นตัวคั่นที่แสดงถึงจุดสิ้นสุดของตัวเลือก]" (? - หมายความว่า " -" เป็นที่ยอมรับว่าเป็นจุดสิ้นสุดของข้อโต้แย้ง[ซึ่งผมจะไม่ได้เกิดขึ้นกับกรณีทดสอบที่ดีสำหรับการอยู่แล้ว แต่ฉันเชือนแชใช้ซึ่งคุณจะ IMO,... [ดูหรูหรา แต่testisclearly คำสั่ง)
JamesTheAwesomeDude

35

ใช่มีความแตกต่าง แบบพกพามากที่สุดคือหรือtest [ ]เหล่านี้มีทั้งส่วนหนึ่งของPOSIXtestสเปค

if ... fiสร้างยังเป็นที่ที่กำหนดโดย POSIXและควรจะพกพาอย่างสมบูรณ์

[[ ]]เป็นkshคุณลักษณะที่ยังมีอยู่ในบางรุ่นbash( คนที่ทันสมัย ) ในzshและบางทีอาจจะในคนอื่น ๆ แต่ไม่ได้อยู่ในshหรือdashหรือเปลือกหอยต่างๆง่ายอื่น ๆ

ดังนั้นเพื่อให้สคริปต์ของคุณแบบพกพาใช้งาน[ ], หรือtestif ... fi


10
เพียงเพื่อทราบbashและzshได้รับการสนับสนุน[[เป็นเวลานานมาก ( bashเพิ่มในช่วงปลายยุค 90, zshไม่เกินปี 2000 และผมต้องการจะประหลาดใจถ้ามันเคยสนับสนุนขาด) [[ดังนั้นคุณจะมีโอกาสที่จะพบรุ่นของทั้งสองโดยไม่ต้อง การพบเชลล์ที่สอดคล้องกับ POSIX อื่น (เช่นdash) มีแนวโน้มที่จะไกลกว่า
chepner

1
หนึ่งในคุณสมบัติที่น่าสนใจ[[คือไม่ต้องมีการอ้างถึงการขยายพารามิเตอร์: [[-f $file]]เหตุการณ์ใช้งานได้หาก$fileมีอักขระช่องว่าง
helpermethod

2
@helpermethod สร้างการแสดงออกปกติด้วย[[ $a =~ ^reg.*exp.*$' ]]
GnP

20

โปรดทราบว่า[] && cmdไม่เหมือนกับif .. fiงานก่อสร้าง

บางครั้งพฤติกรรมของมันสวยคล้ายกันและคุณสามารถใช้แทน[] && cmd if .. fiแต่บางครั้งเท่านั้น หากคุณมีมากกว่าหนึ่งคำสั่งที่จะดำเนินการถ้าเงื่อนไขหรือคุณต้องif .. else .. fiระวังและ whatch ตรรกะ

ตัวอย่างสองตัวอย่าง:

[ -z "$VAR" ] && ls file || echo wiiii

ไม่เหมือนกัน

if [ -z $VAR ] ; then
  ls file
else
  echo wiii
fi

เพราะถ้าlsจะล้มเหลวจะถูกดำเนินการซึ่งจะไม่เกิดขึ้นกับechoif

ตัวอย่างอื่น:

[ -z "$VAR" ] && ls file && echo wiii

ไม่เหมือนกัน

if [ -z "$VAR" ] ; then
   ls file
   echo $wiii
fi

แม้ว่าการก่อสร้างนี้จะทำหน้าที่เดียวกัน

[ -z "$VAR" ] && { ls file ; echo wiii ; }

โปรดทราบ;หลังจากเสียงสะท้อนเป็นสิ่งสำคัญและจะต้องมี

ดังนั้นการกลับมาใช้คำกล่าวข้างต้นเราสามารถพูดได้

[] && cmd == หากคำสั่งแรกสำเร็จแล้วให้ดำเนินการคำสั่งถัดไป

if .. fi == หากเงื่อนไข (ซึ่งอาจเป็นคำสั่งทดสอบเช่นกัน) จากนั้นดำเนินการคำสั่ง (s)

ดังนั้นสำหรับการพกพาระหว่าง[และ[[ใช้งาน[เท่านั้น

ifเข้ากันได้กับ POSIX ดังนั้นหากคุณต้องเลือกระหว่าง[และifเลือกดูงานของคุณและพฤติกรรมที่คาดหวัง


ฉันอาจจะหนาแน่น แต่ฉันไม่เห็นความแตกต่างจะเป็นอย่างไร ... คุณช่วยยกตัวอย่างที่การก่อสร้างทั้งสองจะให้ผลลัพธ์ที่ต่างกันได้ไหม
evilsoup

1
@evilsoup อัปเดตแล้ว อาจเป็นฉันไม่ใช่นักสำรวจที่ดีที่สุด แต่ฉันหวังว่ามันจะชัดเจนในขณะนี้
เร่ง

1
คะแนนที่ดีมากรีบเร่ง ผมคิดว่าเป็นรูปแบบที่ปลอดภัยของตัวอย่างที่ 1 [ -z "$VAR" ] && { ls file; true; } || echo wiiiiของคุณจะเป็น: มันค่อนข้างละเอียดมากขึ้น แต่ก็ยังสั้นกว่าการif...fiก่อสร้าง
PM 2Ring

ทำให้คำถามเป็นเรื่องเกี่ยวกับ [vs [[ทดสอบ vs และไม่เกี่ยวกับว่า ... fi vs && เพื่อให้เป็นคำถามเดียว น่าเสียดายที่การทำเช่นนั้นทำให้คำตอบนี้ดูเหมือนไม่ตรงประเด็น ขออภัยที่ไม่ได้รับคำถามที่มุ่งเน้นไปที่สิ่งนี้ เรียนรู้และ (ลอง) เรียนรู้ :)
Michael Durrant

ฉันลงคะแนนเพราะก) นี่เป็นคำตอบที่ดีเป็นส่วนใหญ่ แต่เป็นคำตอบสำหรับคำถามที่แตกต่าง b) คุณนำเสนอ[และifเป็นคำบรรยาย แต่ไม่ใช่ มันเป็นเรื่องจริงที่เปลี่ยน&& รันรหัสบางอย่างและส่งกลับสถานะเหมือนและจะ การประมวลผลแบรนช์ขึ้นอยู่กับสถานะการส่งคืนของคำสั่ง (คำสั่ง) ที่กำหนดหลังจาก if มันอาจเป็นคำสั่งใด ๆ (คำสั่ง) รันคำสั่งต่อไปเท่านั้นถ้าก่อนหน้านี้หนึ่งกลับ 0 เหมือนที่เรียบง่าย if[lsgrepif&&if..then..fi
GnP

9

จริงๆแล้ว&&มันคือการแทนที่if, ไม่ใช่test: ifคำสั่งในเชลล์สคริปต์การทดสอบว่าคำสั่งส่งกลับสถานะทางออก "ประสบความสำเร็จ" (ศูนย์); [ในตัวอย่างของคำสั่งคือ

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

คำสั่งทดสอบ:

  • testเป็นคำสั่งที่ได้มาตรฐานสำหรับการประเมินคุณสมบัติของสตริงและไฟล์ ในตัวอย่างของคุณคุณกำลังเรียกใช้คำสั่งtest -w /home/durrantm
  • [เป็นนามแฝงของคำสั่งนั้นซึ่งได้มาตรฐานอย่างเท่าเทียมกันซึ่งมีอาร์กิวเมนต์สุดท้ายที่บังคับใช้]เพื่อให้ดูเหมือนนิพจน์ที่อยู่ในวงเล็บ อย่าหลงกลมันเป็นเพียงคำสั่ง (คุณอาจพบว่าระบบของคุณมีไฟล์ชื่อ/bin/[)
  • [[เป็นเวอร์ชันเพิ่มเติมของคำสั่งทดสอบที่สร้างขึ้นในเชลล์บางตัว แต่ไม่ได้เป็นส่วนหนึ่งของมาตรฐาน POSIX เดียวกัน มันมีตัวเลือกเพิ่มเติมที่คุณไม่ได้ใช้ที่นี่

นิพจน์เงื่อนไข:

  • &&ประกอบการ ( มาตรฐานที่นี่ ) ดำเนินการตรรกะและการดำเนินงานโดยการประเมินทั้งสองคำสั่งและกลับมา 0 (ซึ่งหมายถึงจริง) ถ้าพวกเขาทั้งสองกลับมา 0; มันจะประเมินเฉพาะคำสั่งที่สองหากคำสั่งแรกคืนค่าศูนย์ดังนั้นจึงสามารถใช้เป็นเงื่อนไขแบบง่ายได้
  • if ... then ... fiสร้าง ( มาตรฐานที่นี่ ) ใช้วิธีการเดียวกันในการตัดสิน "ความจริง" แต่ช่วยให้สำหรับรายการสารประกอบของงบในthenประโยคมากกว่าคำสั่งเดียว afforded โดย&&ลัดวงจรและให้การelifและelseข้อซึ่งเป็นเรื่องยากที่จะเขียน ใช้เท่านั้น&&และ||. ทราบว่ามีวงเล็บรอบเงื่อนไขที่ไม่มีในifคำสั่ง

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

  • test -w /home/durrantm && echo "writable"
  • [ -w /home/durrantm ] && echo "writable"
  • if test -w /home/durrantm; then echo "writable"; fi
  • if [ -w /home/durrantm ]; then echo "writable"; fi

ในขณะที่ต่อไปนี้ยังเทียบเท่า แต่พกพาน้อยลงเนื่องจากลักษณะที่ไม่ได้มาตรฐานของ[[:

  • [[ -w /home/durrantm ]] && echo "writable"
  • if [[ -w /home/durrantm ]]; then echo "writable"; fi

ใช่ฉันลบส่วน if fi เพื่อให้เป็น 1 คำถาม
Michael Durrant

7

สำหรับการพกพาใช้งาน/test [แต่ถ้าคุณไม่จำเป็นต้องพกพาเพื่อประโยชน์ของสติของตัวเองและคนอื่น ๆ [[การอ่านการใช้สคริปต์ของคุณ :)

ดูWhat is the difference between test, [ and [[ ?ที่BashFAQด้วย


7

หากคุณต้องการความสะดวกในการพกพานอกโลกที่เหมือน Bourne แล้ว:

test -w /home/durrantm && echo writable

เป็นพกพามากที่สุด มันทำงานในเปลือกของบอร์นcshและrcครอบครัว

test -w /home/durrantm && echo "writable"

เอาท์พุทหากว่า"writable"แทนwritableในเปลือกหอยของrcครอบครัว ( rc, es, akangaที่"ไม่ได้เป็นพิเศษ)

[ -w /home/durrantm ] && echo writable

จะไม่ทำงานในเชลล์ของcshหรือrcตระกูลบนระบบที่ไม่มี[คำสั่งใน$PATH(บางคนรู้ว่ามีtestแต่ไม่ใช่[นามแฝง)

if [ -w /home/durrantm ]; then echo writabe; fi

ใช้งานได้กับกระสุนของตระกูล Bourne เท่านั้น

[[ -w /home/durrantm ]] && echo writable

ใช้ได้เฉพาะในksh(ที่มา) zshและbash(ทั้ง 3 ในตระกูล Bourne)

ไม่มีใครทำงานในfishเชลล์ที่คุณต้องการ:

[ -w /home/durrantm ]; and echo writable

หรือ:

if [ -w /home/durrantm ]; echo writable; end

0

เหตุผลที่สำคัญที่สุดของฉันสำหรับการเลือกอย่างใดอย่างหนึ่งif foo; then bar; fiหรือfoo && barว่าสถานะการออกของคำสั่งทั้งหมดนั้นสำคัญหรือไม่

เปรียบเทียบ:

#!/bin/sh
set -e
foo && bar
do_baz

ด้วย:

#!/bin/sh
set -e
if foo; then bar; fi
do_baz

คุณอาจคิดว่าพวกเขาทำเช่นเดียวกัน อย่างไรก็ตามหากfooล้มเหลว (หรือเป็นเท็จขึ้นอยู่กับมุมมองของคุณ) จากนั้นในตัวอย่างแรก do_baz จะไม่ถูกดำเนินการตามที่สคริปต์จะออก ... สคริปต์set -eสั่งให้เชลล์ออกจากทันทีหากคำสั่งใดส่งคืนสถานะเท็จ มีประโยชน์มากถ้าคุณทำสิ่งต่าง ๆ เช่น:

cd /some/directory
rm -rf *

คุณไม่ต้องการให้สคริปต์ทำงานต่อไปหากcdล้มเหลวไม่ว่าด้วยเหตุผลใด ๆ


1
ไม่มีความล้มเหลวfooจะไม่ยกเลิกสคริปต์ในกรณีใดกรณีหนึ่ง นั่นเป็นกรณีพิเศษสำหรับset -e(เมื่อคำสั่งถูกประเมินเป็นเงื่อนไข (ทางด้านซ้ายของเงื่อนไข && / | | หรือในเงื่อนไขว่าในขณะที่ / ถึง / จนกระทั่ง / elsif ... ) ความล้มเหลวbarจะออกจากเชลล์ในทั้งสองกรณี
Stéphane Chazelas

2
คุณต้องการcd /some/directory && rm -rf -- *หรือcd /some/directory || exit; rm -rf -- *(ยังคงไม่ลบไฟล์ที่ซ่อนอยู่) โดยส่วนตัวฉันไม่ชอบความคิดที่จะใช้set -eเป็นข้ออ้างที่จะไม่พยายามเขียนโค้ดที่ถูกต้อง
Stéphane Chazelas
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.