ทำไม `['เชลล์สร้างขึ้นในตัวและ` [[' คำสำคัญของเชลล์]


64

เท่าที่ฉันรู้[[เป็นรุ่นปรับปรุง[แต่ฉันสับสนเมื่อฉันเห็น[[ว่าเป็นคำหลักและ[ถูกแสดงเป็น builtin

[root@server ~]# type [
[ is a shell builtin
[root@server ~]# type [[
[[ is a shell keyword

TLDPพูดว่า

builtin อาจเป็นคำพ้องความหมายของคำสั่งระบบที่มีชื่อเดียวกัน แต่ Bash นำมาใช้ใหม่ภายใน ตัวอย่างเช่นคำสั่ง Bash echo ไม่เหมือนกับ / bin / echo แม้ว่าพฤติกรรมของพวกเขาจะเกือบเหมือนกัน

และ

คำหลักคือคำโทเค็นหรือตัวดำเนินการที่สงวนไว้ คำหลักมีความหมายพิเศษกับเชลล์และแน่นอนว่าเป็นหน่วยการสร้างของไวยากรณ์ของเชลล์ เป็นตัวอย่างสำหรับในขณะที่ทำและ! เป็นคำหลัก เช่นเดียวกับ builtin คีย์เวิร์ดนั้นยากที่จะเขียนลงใน Bash แต่ต่างจาก builtin คำสำคัญนั้นไม่ได้อยู่ในคำสั่ง แต่เป็นหน่วยย่อยของคำสั่งสร้าง [2]

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



9
/ bin / [มีอยู่ในเครื่องของฉัน
Joshua

2
ในฐานะที่เป็นสาธิตที่เรียบง่ายของหนึ่งความแตกต่างระหว่างสอง: if "[" $x -eq 3 ]ทำงานตามที่คาดไว้ (เพราะทุบตีมองหาคำสั่งที่เรียกว่า[และนี้มีอยู่) แต่if "[[" $x -eq 3 ]]ไม่ได้ทำงาน (เพราะอีกครั้งค้นหาทุบตีสำหรับคำสั่งของชื่อที่เหมาะสม แต่ไม่มี[[คำสั่ง)
Kyle Strand

1
@Joshua ดังนั้น/usr/bin/echoแต่ไม่ได้หมายความว่ามันไม่ได้เป็นbuiltin
Jonathon Reinhart

บูลินทั้งหมดที่ยังมีอยู่ในการแยกวิเคราะห์ภายนอกถ้าพวกเขาไม่ได้สร้าง
Joshua

คำตอบ:


80

ความแตกต่างระหว่าง[และ[[ค่อนข้างพื้นฐาน

  • [เป็นคำสั่ง อาร์กิวเมนต์ของมันถูกประมวลผลตามที่ประมวลผลด้วยคำสั่งอื่น ๆ ตัวอย่างเช่นพิจารณา:

    [ -z $name ]

    เปลือกจะขยาย$nameและดำเนินการทั้งสองแยกคำและการสร้างชื่อไฟล์ผลเช่นเดียวกับที่มันจะสำหรับคำสั่งอื่น ๆ

    ดังตัวอย่างต่อไปนี้จะล้มเหลว:

    $ name="here and there"
    $ [ -n $name ] && echo not empty
    bash: [: too many arguments

    เพื่อให้งานนี้ถูกต้องจำเป็นต้องมีการเสนอราคา:

    $ [ -n "$name" ] && echo not empty
    not empty
  • [[คือคีย์เวิร์ดเชลล์และอาร์กิวเมนต์จะถูกประมวลผลตามกฎพิเศษ ตัวอย่างเช่นพิจารณา:

    [[ -z $name ]]

    เชลล์จะขยายตัว$nameแต่ไม่เหมือนกับคำสั่งอื่น ๆ มันจะไม่ทำการแยกคำหรือสร้างชื่อไฟล์ในผลลัพธ์ ตัวอย่างเช่นสิ่งต่อไปนี้จะสำเร็จแม้จะมีช่องว่างฝังอยู่ในname:

    $ name="here and there"
    $ [[ -n $name ]] && echo not empty
    not empty

สรุป

[ เป็นคำสั่งและอยู่ภายใต้กฎเดียวกันกับคำสั่งอื่น ๆ ทั้งหมดที่เชลล์ดำเนินการ

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


+1 ขอบคุณ คุณสามารถให้แหล่งที่มา (อ้างอิง) ภายใต้กฎคำสั่งและคำหลักทำงานอะไร
ทิม

@ Tim กฎภายใต้คำสั่งman bashทำงานมีรายละเอียดใน โดยเฉพาะอย่างยิ่งดูหัวข้อ "การขยายคำสั่งที่เรียบง่าย" และ "การดำเนินการตามคำสั่ง" นอกเหนือจากการ[[ทุบตีอื่น ๆคำหลักได้แก่if, then, whileและ 'กรณี' ไม่มีกฎทั่วไปสำหรับคำหลัก: คำหลักแต่ละคำเป็นกรณีพิเศษ man bash รวมถึงรายละเอียดสำหรับแต่ละ
John1024

62

ในV7 Unix - กรณีที่บอร์นเชลล์เปิดตัว - [ถูกเรียกและมันมีตัวตนเป็นเพียงtest /bin/testดังนั้นรหัสที่คุณจะเขียนวันนี้เป็น:

if [ "$foo" = "bar" ] ; then ...

คุณน่าจะเขียนแทนเป็น

if test "$foo" = "bar" ; then ...

สัญกรณ์ที่สองนี้ยังคงมีอยู่และฉันพบว่ามันชัดเจนมากขึ้นเกี่ยวกับสิ่งที่เกิดขึ้น: คุณกำลังเรียกคำสั่งที่เรียกว่าtestซึ่งประเมินข้อโต้แย้งของมันและส่งกลับรหัสสถานะทางออกที่ifใช้ในการตัดสินใจว่าจะทำอย่างไรต่อไป คำสั่งนั้นอาจถูกสร้างไว้ในเชลล์หรืออาจเป็นโปรแกรมภายนอก

[เป็นทางเลือกที่จะtestมาภายหลัง²มันอาจจะเป็นคำพ้องใน builtin testแต่มันก็มีให้/bin/[ในระบบที่ทันสมัยสำหรับเปลือกที่ไม่ได้เป็น builtin

[และtestอาจนำมาใช้โดยใช้รหัสเดียวกัน นี่เป็นกรณีสำหรับ/bin/[และ/bin/testใน OS X ซึ่งสิ่งเหล่านี้เป็นฮาร์ดลิงก์ไปยังไฟล์ปฏิบัติการเดียวกัน³ด้วยเหตุนี้การนำไปปฏิบัติจะละเว้นการติดตามอย่างสมบูรณ์]: มันไม่จำเป็นต้องใช้ถ้าคุณเรียกมันว่า/bin/[และมันไม่บ่น ถ้าคุณทำให้มันให้/bin/test.⁴

ไม่มีของประวัติศาสตร์ที่มีผลต่อเพราะมีไม่เคยเป็นโปรแกรมแรกที่เรียกว่า[[ [[มันมีอยู่อย่างหมดจดภายในเปลือกหอยที่ใช้มันเป็นส่วนขยายไปยังเปลือก POSIX

ส่วนหนึ่งของความแตกต่างระหว่าง "builtin" และ "คำหลัก" เป็นเพราะประวัติศาสตร์นี้ นอกจากนี้ยังสะท้อนให้เห็นถึงความจริงที่ว่ากฎไวยากรณ์สำหรับการแยกวิเคราะห์[[นิพจน์แตกต่างกันตามที่ระบุไว้ในคำตอบของ John1024


เชิงอรรถ:

  1. เมื่อคุณมองอย่างนั้นมันชัดเจนว่าทำไมคุณต้องใส่ช่องว่าง[ในเชลล์สคริปต์ซึ่งแตกต่างจากวิธีที่วงเล็บและวงเล็บทำงานในภาษาโปรแกรมอื่น ๆ ส่วนใหญ่ หากตัวแยกวิเคราะห์คำสั่งของเปลือกอนุญาตให้ใช้if["$x"...ก็จะต้องอนุญาตiftest"$x"...

  2. มันเกิดขึ้นประมาณปี 1980 /bin/[ไม่มีอยู่ในสำเนาAncient Unix V7ของฉันตั้งแต่ปี 1979 และไม่มีman testเอกสารเป็นนามแฝง ในรายการ man page ที่สอดคล้องกันที่ฉันมีในสำเนา pre-release ของคู่มือSystem IIIจากปี 1980 มันอยู่ในรายการ

  3. ls -i /bin/[ /bin/test

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

  5. ความแตกต่างของคำสั่ง builtin vs external อาจมีเหตุผลอื่นด้วยเช่นกัน: การใช้งานสองแบบอาจทำงานต่างกัน นี่เป็นกรณีสำหรับechoหลาย ๆ ระบบ เนื่องจากมีการนำไปใช้เพียงครั้งเดียวจึงไม่จำเป็นต้องแยกความแตกต่างดังกล่าวสำหรับคำหลัก


ขอบคุณ @Warren Young แต่ทำไมbuiltinและkeywordความแตกต่างระหว่าง[และ[[เมื่อทั้งสองมีฟังก์ชันการทำงานเดียวกัน (ยกเว้นความจริงที่[[มาพร้อมกับคุณสมบัติที่มากกว่า[)
Sree

2
@sree [[การเป็นคีย์เวิร์ดช่วยให้ bash สามารถทำสิ่งที่ไม่สามารถทำได้[ตัวอย่างเช่นการอ้างไม่จำเป็นต้องใช้เวลามากเพราะเชลล์รู้ว่ามันเป็นตัวแปร กล่าวคือการประมวลผลบรรทัดคำสั่งจะได้รับผลกระทบเมื่อมีการใช้คีย์เวิร์ด แต่ไม่ใช่เมื่อใช้บิวอิน - ซึ่งจะเกิดขึ้นในภายหลัง
muru

1
โปรดทราบว่ารหัสสำหรับเชลล์ V7 Bourne จะแสดง[บิวด์อิน แต่โค้ดจะใส่ความคิดเห็นไว้
Stéphane Chazelas

cdเป็น builtin แต่ไม่ทำอะไรเลย ( cdไม่สามารถใช้งานเป็นโปรแกรมภายนอกได้)
Paŭlo Ebermann

2
นี่เป็นบทสรุปทางประวัติศาสตร์ที่ยอดเยี่ยม แต่มันทำให้รายละเอียดที่สำคัญ (IMO) ชี้โดย muru ด้านบนและ John1024 ในคำตอบของพวกเขาการสร้าง[[คำหลักทำให้เชลล์สามารถใช้กฎการแยกวิเคราะห์พิเศษสำหรับอาร์กิวเมนต์ของมัน อนิจจา upvote ของฉันไปที่ John1024
Ilmari Karonen

3

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

[[มามากในภายหลัง แม้ว่า builtin จะถูกใช้งานภายในภายในเชลล์ แต่จะถูกวิเคราะห์คำเหมือนกับคำสั่งภายนอก ดังที่อธิบายไว้ในคำตอบของ John1024 ซึ่งหมายความว่าตัวแปรที่ไม่มีเครื่องหมายจะได้รับการแบ่งคำบนพวกเขาและโทเค็นที่ชอบ>และ<ถูกประมวลผลตามการเปลี่ยนเส้นทาง I / O สิ่งนี้ทำให้การเขียนนิพจน์เปรียบเทียบที่ซับซ้อนไม่สะดวก [[ถูกสร้างขึ้นเป็นไวยากรณ์ของเชลล์เพื่อให้สามารถแยกวิเคราะห์ ideosyncratically ภายใน[[ตัวแปรไม่ได้รับการแบ่งคำ<และ>สามารถใช้เป็นตัวดำเนินการเปรียบเทียบ=สามารถทำงานแตกต่างกันไปขึ้นอยู่กับว่ามีการอ้างอิงพารามิเตอร์ถัดไปหรือไม่ ฯลฯ สิ่งเหล่านี้อำนวยความสะดวกทั้งหมดที่ทำให้[[ใช้งานได้ง่ายกว่า[คำสั่ง / builtin ดั้งเดิม

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

สิ่งนี้คล้ายกับวิวัฒนาการที่ทำให้เกิด$((...))ไวยากรณ์สำหรับนิพจน์ทางคณิตศาสตร์ซึ่งส่วนใหญ่ได้แทนที่exprคำสั่งดั้งเดิม


0

ใหม่[[ในการbashเป็นการเพิ่มประสิทธิภาพ[ของ

คลาสสิก[มีข้อเสียเปรียบครั้งใหญ่เมื่อมีการใช้งานบ่อยครั้งในการทำงานเล็กน้อย: มันจะวางไข่กระบวนการใหม่ทุกครั้ง:
(มันสร้างพื้นที่ที่อยู่ใหม่สำหรับการเปรียบเทียบ0และ1! ทุกครั้ง!)

ฉันคิดว่าประเด็นหลักของการเพิ่ม[[คือการประเมินการแสดงออกภายใน[ไม่เกิดกระบวนการพิเศษ แต่วิธีการ[ทำงานไม่สามารถเปลี่ยนแปลงได้ - มันจะสร้างความสับสนและปัญหามากมาย ดังนั้นการปรับให้เหมาะสมได้รับการใช้งานด้วยชื่อใหม่ในวิธีที่มีประสิทธิภาพมากขึ้นคือคำสั่ง shell builtin
มันกลายเป็นคำหลักในไวยากรณ์เชลล์เป็นผลข้างเคียง

ในเวลานั้น[ถูกใช้ครั้งแรกมันเป็นวิธีที่ถูกต้องในการทำกระบวนการภายนอก


5
โปรดทราบว่าแม้ว่า[เดิมเป็นคำสั่งจากภายนอกมันถูกเพิ่มเป็นบิวด์อินในตัวเชลล์ แต่เนิ่น ๆ โดยอาจใช้เวลาที่ Unix System III เปิดตัวและแน่นอนก่อนที่ Unix System V จะถูกปล่อยออกมา ดังนั้น 'กระบวนการพิเศษ' จึงไม่เป็นปัญหาสำหรับทุกวัย อย่างไรก็ตามไวยากรณ์ยังคงไม่เปลี่ยนแปลง - มันได้รับการปฏิบัติราวกับว่ามันจะเป็นคำสั่งภายนอก
Jonathan Leffler

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