เพื่อนร่วมงานอ้างสิทธิ์เมื่อเร็ว ๆ นี้ในการตรวจสอบโค้ดว่าสิ่ง[[ ]]
ก่อสร้างนั้นเป็นที่ต้องการมากกว่า[ ]
ในงานสร้างเช่น
if [ "`id -nu`" = "$someuser" ] ; then
echo "I love you madly, $someuser"
fi
เขาไม่สามารถให้เหตุผลได้ มีไหม
เพื่อนร่วมงานอ้างสิทธิ์เมื่อเร็ว ๆ นี้ในการตรวจสอบโค้ดว่าสิ่ง[[ ]]
ก่อสร้างนั้นเป็นที่ต้องการมากกว่า[ ]
ในงานสร้างเช่น
if [ "`id -nu`" = "$someuser" ] ; then
echo "I love you madly, $someuser"
fi
เขาไม่สามารถให้เหตุผลได้ มีไหม
คำตอบ:
[[
มีความประหลาดใจน้อยลงและโดยทั่วไปจะปลอดภัยกว่าการใช้งาน แต่มันไม่สามารถพกพาได้ - POSIX ไม่ได้ระบุสิ่งที่มันทำและมีเพียงหอยบางตัวเท่านั้นที่สนับสนุน (นอกเหนือจาก bash ฉันได้ยิน ksh ก็สนับสนุนเช่นกัน) ตัวอย่างเช่นคุณสามารถทำได้
[[ -e $b ]]
เพื่อทดสอบว่ามีไฟล์อยู่หรือไม่ แต่ด้วยความที่[
คุณต้องอ้าง$b
เพราะมันแยกอาร์กิวเมนต์และขยายสิ่งที่ชอบ"a*"
(ที่[[
ใช้มันตัวอักษร) สิ่งนั้นเกี่ยวข้องกับวิธีที่[
จะเป็นโปรแกรมภายนอกและรับอาร์กิวเมนต์โดยปกติเหมือนกับโปรแกรมอื่น ๆ ทุกตัว (แม้ว่ามันจะเป็น builtin แต่ก็ยังไม่มีการจัดการพิเศษนี้)
[[
นอกจากนี้ยังมีคุณสมบัติที่ดีอื่น ๆ เช่นการจับคู่นิพจน์ทั่วไป=~
พร้อมกับตัวดำเนินการเหมือนกับที่รู้จักในภาษา C-like นี่คือหน้าดีเกี่ยวกับมัน: อะไรคือความแตกต่างระหว่างการทดสอบ[
และ[[
? และการทดสอบทุบตี
[[ ]]
[ ]
#!/bin/sh
แต่จากนั้นสลับไปใช้#!/bin/bash
ทันทีที่ฉันใช้คุณลักษณะเฉพาะของ BASH เพื่อแสดงว่าไม่มี Bourne shell แบบพกพาอีกต่อไป
ความแตกต่างของพฤติกรรม
ความแตกต่างบางประการใน Bash 4.3.11:
POSIX vs Bash extension:
[
POSIX คือ[[
เป็นนามสกุล Bash ¹คำสั่งปกติกับเวทมนตร์
[
เป็นเพียงคำสั่งปกติที่มีชื่อแปลก ๆ
]
เป็นเพียงข้อโต้แย้ง[
ที่ป้องกันไม่ให้มีการใช้ข้อโต้แย้งเพิ่มเติม
จริง ๆ แล้ว Ubuntu 16.04 นั้นสามารถใช้งานได้จริง/usr/bin/[
โดยcoreutilsแต่ bash ในตัวจะมีความสำคัญมากกว่า
ไม่มีการเปลี่ยนแปลงในวิธีที่ Bash วิเคราะห์คำสั่ง
โดยเฉพาะอย่างยิ่ง<
คือการเปลี่ยนเส้นทาง&&
และ||
เชื่อมคำสั่งหลาย ๆ คำสั่ง( )
สร้าง subshells เว้นแต่จะหลบหนีไป\
และการขยายคำจะเกิดขึ้นตามปกติ
[[ X ]]
เป็นโครงสร้างเดียวที่ทำให้การX
แยกวิเคราะห์อย่างน่าอัศจรรย์ <
, &&
, ||
และ()
ได้รับการปฏิบัติเป็นพิเศษและกฎการแยกคำที่แตกต่างกัน
นอกจากนี้ยังมีความแตกต่างต่อไปเหมือนและ=
=~
ใน Bashese: [
เป็นคำสั่งในตัวและ[[
เป็นคำหลัก: /ubuntu/445749/whats-the-difference-between-shell-builtin-and-shell-keyword
<
[[ a < b ]]
: การเปรียบเทียบพจนานุกรม[ a \< b ]
: เหมือนด้านบน \
จำเป็นหรือไม่ก็เปลี่ยนเส้นทางเหมือนคำสั่งอื่น ๆ นามสกุลทุบตีexpr a \< b > /dev/null
: POSIX เทียบเท่า²ดู: วิธีทดสอบสตริงสำหรับ lexicographic น้อยกว่าหรือเท่ากับใน Bash&&
และ ||
[[ a = a && b = b ]]
: จริงตรรกะและ[ a = a && b = b ]
: ข้อผิดพลาดทางไวยากรณ์&&
แยกวิเคราะห์เป็นตัวคั่นคำสั่ง ANDcmd1 && cmd2
[ a = a -a b = b ]
: เทียบเท่า แต่เลิกใช้แล้วโดยPOSIX³[ a = a ] && [ b = b ]
: POSIX และเทียบเท่าที่เชื่อถือได้(
[[ (a = a || a = b) && a = b ]]
: false[ ( a = a ) ]
: ข้อผิดพลาดทางไวยากรณ์()
ถูกตีความว่าเป็น subshell[ \( a = a -o a = b \) -a a = b ]
: เทียบเท่า แต่()
เลิกใช้แล้วโดย POSIX{ [ a = a ] || [ a = b ]; } && [ a = b ]
POSIX ที่เทียบเท่า⁵การแยกคำและการสร้างชื่อไฟล์ตามการขยาย (แยก + glob)
x='a b'; [[ $x = 'a b' ]]
: จริงไม่จำเป็นต้องใส่เครื่องหมายคำพูดx='a b'; [ $x = 'a b' ]
: ข้อผิดพลาดทางไวยากรณ์ขยายเป็น [ a b = 'a b' ]
x='*'; [ $x = 'a b' ]
: ข้อผิดพลาดทางไวยากรณ์หากมีมากกว่าหนึ่งไฟล์ในไดเรกทอรีปัจจุบันx='a b'; [ "$x" = 'a b' ]
: เทียบเท่า POSIX=
[[ ab = a? ]]
: จริงเพราะมันจับคู่รูปแบบ ( * ? [
เป็นเวทมนตร์) ห้าม glob ขยายไฟล์ในไดเรกทอรีปัจจุบัน[ ab = a? ]
: a?
glob ขยายตัว ดังนั้นอาจเป็นจริงหรือเท็จขึ้นอยู่กับไฟล์ในไดเรกทอรีปัจจุบัน[ ab = a\? ]
: เท็จไม่ใช่การขยาย glob=
และ==
เหมือนกันทั้งใน[
และ[[
, แต่==
เป็นส่วนขยายของ Bashcase ab in (a?) echo match; esac
: เทียบเท่า POSIX[[ ab =~ 'ab?' ]]
: false⁴, สูญเสียเวทย์ด้วย ''
[[ ab? =~ 'ab?' ]]
: จริง=~
[[ ab =~ ab? ]]
: จริง POSIX ขยายการจับคู่การแสดงออกปกติ?
ไม่ขยาย[ a =~ a ]
: ข้อผิดพลาดทางไวยากรณ์ ไม่เทียบเท่าทุบตีprintf 'ab\n' | grep -Eq 'ab?'
: เทียบเท่า POSIX (ข้อมูลบรรทัดเดียวเท่านั้น)awk 'BEGIN{exit !(ARGV[1] ~ ARGV[2])}' ab 'ab?'
: เทียบเท่า POSIXคำแนะนำ : []
มักจะใช้
มี POSIX ที่เทียบเท่าสำหรับการ[[ ]]
สร้างทุกครั้งที่ฉันเห็น
ถ้าคุณใช้[[ ]]
คุณ:
[
เป็นเพียงคำสั่งปกติที่มีชื่อแปลก ๆ ไม่มีความหมายพิเศษเกี่ยวข้อง¹แรงบันดาลใจจากโครงสร้างที่เทียบเท่า[[...]]
ใน Korn เชลล์
² แต่ล้มเหลวสำหรับบางค่าของa
หรือb
(เช่น+
หรือindex
) และทำการเปรียบเทียบตัวเลขถ้าa
และb
มีลักษณะเป็นจำนวนเต็มทศนิยม expr "x$a" '<' "x$b"
ทำงานได้ทั้งสองอย่าง
³และยังล้มเหลวสำหรับค่าของบางส่วนa
หรือb
เหมือนหรือ!
(
⁴ใน bash 3.2 ขึ้นไปและไม่สามารถเปิดใช้งานความเข้ากันได้กับ bash 3.1 (เช่นเดียวกับBASH_COMPAT=3.1
)
⁵ถึงแม้ว่าการจัดกลุ่ม (ที่นี่พร้อมกับ{...;}
กลุ่มคำสั่งแทนที่จะ(...)
เป็นการเรียกใช้ subshell ที่ไม่จำเป็น) ไม่จำเป็นเนื่องจากตัวดำเนินการ||
และ&&
ตัวดำเนินการเชลล์ (ตรงข้ามกับตัวดำเนินการ||
และ&&
[[...]]
ตัวดำเนินการหรือตัวดำเนินการ-o
/ / -a
[
) ดังนั้น[ a = a ] || [ a = b ] && [ a = b ]
จะเทียบเท่า
[
เหตุผลเดียวกัน
[[ ]]
มีคุณสมบัติมากขึ้น - ผมขอแนะนำให้คุณลองดูที่เป็นขั้นสูงทุบตี Scripting คู่มือสำหรับข้อมูลเพิ่มเติมโดยเฉพาะคำสั่งทดสอบการขยายส่วนในบทที่ 7 การทดสอบ
อนึ่งในฐานะบันทึก[[ ]]
แนะนำถูกนำมาใช้ใน ksh88 (เวอร์ชัน 1988 ของเชลล์ Korn)
จากเครื่องมือเปรียบเทียบการทดสอบตัวยึดหรือตัวยึดคู่ใดเร็วที่สุด? ( http://bashcurescancer.com )
วงเล็บคู่เป็น“ คำสั่งผสม” โดยที่การทดสอบและวงเล็บเดี่ยวเป็นเชลล์ในตัว (และในความเป็นจริงคือคำสั่งเดียวกัน) ดังนั้นวงเล็บเดี่ยวและวงเล็บคู่รันรหัสที่แตกต่างกัน
การทดสอบและวงเล็บเดี่ยวเป็นแบบพกพามากที่สุดเนื่องจากมีอยู่เป็นคำสั่งแยกและภายนอก อย่างไรก็ตามหากคุณใช้ BASH เวอร์ชันที่ทันสมัยจากระยะไกลวงเล็บเหลี่ยมสองตัวจะได้รับการสนับสนุน
[[
จะนำมาซึ่ง แต่แล้วฉันเป็นโรงเรียนเก่าผายลมเก่า :-)
[
และtest
แม้ว่าเวอร์ชั่นภายนอกก็มีอยู่
PS1=...crazy stuff...
และ / หรือ$PROMPT_COMMAND
) สำหรับสิ่งเหล่านี้ฉันไม่ต้องการให้เกิดความล่าช้าในการดำเนินการของสคริปต์
apt-get update
ถ้ามันผ่านไปนานกว่า X ชั่วโมงนับตั้งแต่วิ่งครั้งสุดท้าย มันเป็นความโล่งใจที่ยอดเยี่ยมเมื่อหนึ่งสามารถออกจากการพกพาออกจากรายการข้อ จำกัด สำหรับรหัสยาวเกินไปแล้ว
หากคุณกำลังทำตามคู่มือสไตล์ของ Google :
ทดสอบ[
และ[[
[[ ... ]]
ลดข้อผิดพลาดเนื่องจากไม่มีการขยายชื่อพา ธ หรือการแยกคำที่เกิดขึ้นระหว่าง[[
และ]]
และ[[ ... ]]
ช่วยให้การจับคู่นิพจน์ปกติที่[ ... ]
ไม่
# This ensures the string on the left is made up of characters in the
# alnum character class followed by the string name.
# Note that the RHS should not be quoted here.
# For the gory details, see
# E14 at https://tiswww.case.edu/php/chet/bash/FAQ
if [[ "filename" =~ ^[[:alnum:]]+name ]]; then
echo "Match"
fi
# This matches the exact pattern "f*" (Does not match in this case)
if [[ "filename" == "f*" ]]; then
echo "Match"
fi
# This gives a "too many arguments" error as f* is expanded to the
# contents of the current directory
if [ "filename" == f* ]; then
echo "Match"
fi
[[ -d ~ ]]
จะคืนค่าจริง (ซึ่งหมายถึง~
การขยาย/home/user
) ฉันคิดว่า Google ควรมีความแม่นยำมากกว่าในการเขียน
สถานการณ์ทั่วไปที่คุณไม่สามารถใช้งานได้[[
คือสคริปต์ autotools configure.ac มีเครื่องหมายวงเล็บมีความหมายพิเศษและแตกต่างกันดังนั้นคุณจะต้องใช้test
แทน[
หรือ[[
- โปรดสังเกตว่าการทดสอบ[
นั้นเป็นโปรแกรมเดียวกัน
[
ถึงคาดว่าจะถูกกำหนดให้เป็นฟังก์ชัน POSIX เชลล์
[[]] วงเล็บสองชั้นไม่ถูกคัดออกภายใต้รุ่น SunOS บางรุ่นและไม่มีการเรียงลำดับทั้งหมดภายในการประกาศฟังก์ชันโดย: GNU ทุบตีรุ่น 2.02.0 (1) - ปล่อย (sparc-sun-solaris2.6)
สรุป [[ดีกว่าเพราะมันไม่ได้แยกกระบวนการอื่น ไม่มีวงเล็บหรือวงเล็บเดี่ยวช้ากว่าวงเล็บคู่เพราะจะทำกระบวนการอื่น
type [
ดูสิ่งนี้
[[
แตกต่างจาก[
เป็นไวยากรณ์ตีความโดยล่ามบรรทัดคำสั่ง type [[
ในทุบตีลองพิมพ์ unix4linux ถูกต้องที่แม้ว่า[
การทดสอบBourne-shell แบบคลาสสิกจะแยกกระบวนการใหม่เพื่อกำหนดค่าความจริง[[
ไวยากรณ์ (ยืมจาก ksh โดย bash, zsh ฯลฯ ) ไม่ได้
[
มีอยู่แล้วใน Bash และ Dash ( /bin/sh
ในดิสทริบิวชั่นลีนุกซ์จาก Debian ทั้งหมด)
[[
กับมันรหัสเป็นสิ่งที่ดีและชัดเจน แต่จำวันนั้นเมื่อคุณจะพอร์ต scriptworks ของคุณในระบบที่มีเปลือกเริ่มต้นซึ่งไม่ได้bash
หรือksh
, ฯลฯ[
เป็นสิ่งที่น่าเกลียดน่ารำคาญ แต่ทำงานได้AK-47
ในทุกสถานการณ์