ความแตกต่างระหว่าง“ function foo () {}” และ“ foo () {}”


96

ฉันสามารถกำหนดbashฟังก์ชั่นการใช้หรือละเว้นfunctionคำหลักได้ มีความแตกต่างหรือไม่?

#!/bin/bash

function foo() {
  echo "foo"
}

bar() {
  echo "bar"
}

foo

bar

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

BTW ในเชลล์อื่น ๆ เช่นdash(เชื่อม/bin/shโยงกับdashเดเบียน / Ubuntu) มันล้มเหลวเมื่อใช้functionคำหลัก


8
function baz { echo "baz"; }นอกจากนี้ยังมีหรือไม่มีวงเล็บ: ดูBashismในวิกิของ GreyCat
จัดการ

คำตอบ:


42

ไม่มีความแตกต่าง AFAIK นอกเหนือจากความจริงที่ว่ารุ่นที่สองเป็นแบบพกพามากขึ้น


32
นั่นเป็นข้อแตกต่างที่ใหญ่มากพกพาได้ ... ฉันเห็นคำตอบมากมายที่ไม่สามารถใช้กับระบบการผลิต (เก่ากว่า) และยังมีตัวเลือกมากมายที่ใช้งานได้กับ Linux เท่านั้น อย่างน้อยเตือนว่านี่ไม่ใช่วิธีพกพาที่สุด ... มันอาจเป็นอันตรายอย่างจริงจัง (ขอให้คนที่tar cf - /some/thing | ssh user@desthost "cd destinationdir && tar xf - " ไม่มีการเตือนพวกเขาให้ตรวจสอบครั้งแรกหากรุ่น tar บน desthost จะกำจัด "/" อาจนำไปสู่ ภัยพิบัติในบางกรณี ... ) สำหรับอดีต: ถ้าใช้งานfunction tar { #a safe tar with safety checks ... }และshไม่สนใจมัน ...
โอลิเวีย Dulac

1
@OlivierDulac ฉันจะเพิ่มสคริปต์ที่ถือว่าshจดจำfunctionคำหลัก - และโดยทั่วไปแล้วจะถือว่าคุณสมบัติทั่วไป แต่ไม่เป็นมาตรฐานที่เชลล์ชอบkshและbashเสนอ - มักจะไม่ทำงานในระบบการผลิตที่ใหม่กว่าแม้ว่าพวกเขาจะทำงานในรุ่นเก่ากว่า ระบบปฏิบัติการเดียวกัน bashยังคงมีshอยู่ในระบบ GNU / Linux จำนวนมาก แต่ distros ที่นิยมบางส่วนได้เปลี่ยนมาshใช้เป็น symlink ไปที่dash(Debian Almquist SHell) เพื่อปรับปรุงประสิทธิภาพ ซึ่งรวมถึงDebianและUbuntu
Eliah Kagan

2
โดยไม่ต้องอธิบายว่ามัน "พกพามากกว่านี้" ได้อย่างไรคำตอบนั้นไม่มีประโยชน์
ivan_pozdeev

ความสะดวกในการพกพาไม่มีข้อโต้แย้งทุกวันนี้ที่อุตสาหกรรมใช้ bash หรือ shell ที่เทียบเท่าใน> 99.9% ของสภาพแวดล้อมทั้งหมด มันทำให้การอ่านง่ายขึ้นและมีสองด้าน: บางคนพูดว่า "ฟังก์ชั่น" ทำให้ชัดเจนคนที่เรียนรู้จากมาตรฐาน posix หรือกระสุนอื่น ๆ จะพูดว่าวงเล็บปีกกาเป็นรูปแบบที่ชัดเจนกว่า ในท้ายที่สุดเลือกหนึ่งรายการภายในกลุ่มหรือ บริษัท ของคุณแล้วทำตาม
Hubert Grzeskowiak

92

functionคำหลักที่เป็นที่รู้จักในksh เชลล์เป้าหมายดั้งเดิมมีเพียงfoo ()ไวยากรณ์และPOSIX จะสร้างมาตรฐานเฉพาะfoo ()ไวยากรณ์

ใน ATT ksh (แต่ไม่ใช่ pdksh) มีความแตกต่างเล็กน้อยระหว่างฟังก์ชั่นที่กำหนดโดยfunctionและฟังก์ชั่นที่กำหนดด้วยไวยากรณ์ Bourne / POSIX ในฟังก์ชั่นที่กำหนดโดยfunctionที่typesetคำหลักประกาศตัวแปรท้องถิ่น: เมื่อออกจากฟังก์ชั่นที่คุ้มค่าของตัวแปรตั้งค่าใหม่เป็นสิ่งที่มันเป็นก่อนที่จะเข้าฟังก์ชั่น ด้วยไวยากรณ์คลาสสิกตัวแปรมีขอบเขตทั่วโลกไม่ว่าคุณจะใช้typesetหรือไม่ก็ตาม

$ ksh -c 'a=global; f () { typeset a=local; }; f; echo $a'
local
$ ksh -c 'a=global; function f { typeset a=local; }; f; echo $a'
global

ความแตกต่างใน ksh ก็คือฟังก์ชั่นที่กำหนดไว้กับfunctionคำหลักที่มีบริบทกับดักของตัวเอง กับดักที่กำหนดไว้นอกฟังก์ชั่นจะถูกละเว้นในขณะที่ดำเนินการฟังก์ชั่นและข้อผิดพลาดร้ายแรงภายในฟังก์ชั่นออกเฉพาะฟังก์ชั่นและไม่ได้มาจากสคริปต์ทั้งหมด นอกจากนี้ยัง$0เป็นชื่อฟังก์ชั่นในการทำงานที่กำหนดโดยแต่ชื่อสคริปต์ในฟังก์ชั่นที่กำหนดไว้ด้วยfunction()

Pdksh ไม่ได้จำลอง ATT ksh ใน pdksh typesetสร้างตัวแปรที่กำหนดขอบเขตแบบโลคัลโดยไม่คำนึงถึงฟังก์ชันและไม่มีกับดักในตัวเครื่อง (แม้ว่าการใช้functionจะสร้างความแตกต่างเล็กน้อย - ดูหน้ารายละเอียด)

Bash และ zsh แนะนำfunctionคำสำคัญสำหรับความเข้ากันได้กับ ksh อย่างไรก็ตามในเปลือกหอยเหล่านี้function foo { … }และfoo () { … }จะเหมือนกันอย่างเคร่งครัดเป็นทุบตีและ zsh function foo () { … }ขยาย typesetคำหลักเสมอประกาศตัวแปรท้องถิ่น (ยกเว้น-gของหลักสูตร), และกับดักไม่ได้ในท้องถิ่น (คุณจะได้รับกับดักในท้องถิ่น zsh โดยการตั้งค่าlocal_trapsตัวเลือก)


8
ควรสังเกตว่าการรองรับฟังก์ชั่นถูกเพิ่มเข้ากับ Korn เชลล์ก่อนที่บอร์นเชลล์จะแนะนำfoo() commandไวยากรณ์ของมันและไวยากรณ์ของบอร์นจะถูกเพิ่มเข้าไปใน Korn เชลล์ในภายหลังเพื่อความเข้ากันได้
Stéphane Chazelas

2
ถูกต้องหรือfunction { ... }; f;ไม่fหลังจากfunctionคำหลักนั้นถูกต้อง
Ruslan

32
foo() any-command

เป็นไวยากรณ์บอร์นได้รับการสนับสนุนโดยใด ๆ บอร์นเชลล์เหมือน แต่bash, yashและรุ่นล่าสุดposh(ซึ่งสนับสนุนเพียงคำสั่งผสม) (เชลล์เป้าหมายและการใช้งาน AT&T ที่kshไม่สนับสนุนfoo() any-command > redirectionsเว้นแต่any-commandเป็นคำสั่งผสม)

foo() any-compound-command

(ตัวอย่างของสารประกอบคำสั่ง: { cmd; }, for i do echo "$i"; done, (cmd)... ถูกนำมาใช้กันมากที่สุด{ ...; })

เป็นไวยากรณ์ POSIX ที่ได้รับการสนับสนุนโดยเชลล์ที่มีลักษณะคล้ายบอร์นใด ๆ และหนึ่งที่คุณต้องการใช้โดยทั่วไป

function foo { ...; }

คือไวยากรณ์ Korn เชลล์ซึ่งมีอยู่ในไวยากรณ์ Bourne ใช้อันนี้เฉพาะเมื่อเขียนโดยเฉพาะสำหรับการปรับใช้ AT&T ของ Korn เชลล์และต้องการการรักษาเฉพาะที่ได้รับ ไวยากรณ์ที่ไม่ POSIX แต่ได้รับการสนับสนุนโดยbash, yashและzshเพื่อให้เข้ากับเปลือกกรแม้ว่าหอยเหล่านั้น (และpdkshสายพันธุ์ชั่นของเปลือกกร) ไม่รักษามันแตกต่างจากรูปแบบมาตรฐาน

function foo () { ...; }

ไวยากรณ์ของไม่มีเปลือกและไม่ควรนำมาใช้ มันจะเกิดขึ้นได้รับการสนับสนุนจากอุบัติเหตุโดยbash, yash, zshและpdkshตัวแปรตามของเปลือกกร บังเอิญมันเป็นawkฟังก์ชั่นไวยากรณ์

ถ้าเราลงไปในรายการที่ลึกลับ

function foo() other-compound-command

(เหมือนfunction foo() (subshell)หรือfunction foo() for i do; ... done) ยิ่งแย่กว่านั้นอีก ได้รับการสนับสนุนโดยbash, yashและzshแต่ไม่ KSH แม้pdkshสายพันธุ์ชั่น

ในขณะที่:

function foo() simple command

zshได้รับการสนับสนุนโดยเฉพาะ


1
ไวยากรณ์ที่มีทั้งfunctionคำหลักและวงเล็บถูกบันทึกไว้ใน Bash คู่มือการทุบตี 4.2 และต่อมาบอกว่าฟังก์ชั่นได้รับการประกาศโดยไวยากรณ์หรือname () compound-command [ redirections ] function name [()] compound-command [ redirections ]ใน Bash 4.1.11 จนถึงอย่างน้อย 3.0-beta ที่เป็นเพียงบรรทัดเดียว[ function ] name () compound-command [redirection]ซึ่งไม่ครอบคลุมไวยากรณ์ที่มีfunctionคำหลัก แต่ไม่รวมวงเล็บ แต่ยังครอบคลุมไวยากรณ์ที่มีทั้งfunctionคำหลักและวงเล็บ
nisetama

@nise ประเด็นก็คือการbashรับรู้function foo {นอกเหนือจากfoo() {ความเข้ากันได้กับ Korn เชลล์ (และมีเสมอ) เพื่อให้สามารถตีความสคริปต์ที่เขียนสำหรับ Korn เชลล์ มันรองรับfunction foo () {เช่นกัน แต่ไม่มีเหตุผลที่ดีที่จะใช้อันนั้น
Stéphane Chazelas

2
@ function f() {StéphaneChazelasดีผมจะบอกว่ามีเหตุผลที่ดีที่จะใช้ กล่าวคือในแง่ของความสามารถในการอ่านมันจะได้รับการยอมรับว่าเป็นฟังก์ชั่นโดยทุกคนที่รู้ภาษาอังกฤษและทุกคนที่รู้จัก C กับชุดเดียวเท่านั้น
DepaniDaniel

2
ควรเป็นคำตอบที่ยอมรับได้ สำคัญมากYou should never combine the keyword function with the parentheses () when defining a function.
4wk_

ยินดีต้อนรับสู่ 2019 ซึ่งมีเพียงหนึ่งอุตสาหกรรม de-facto มาตรฐานสำหรับสคริปต์อัตโนมัติ linux / unix; ล่ามตัวหนึ่งที่ติดตั้งแทบทุกที่ (สภาพแวดล้อมแปลกใหม่): bash เขียนรหัสของคุณด้วยคุณสมบัติทุบตีทั้งหมดใช้ shebang และคุณจะไม่เป็นไร อาร์กิวเมนต์ของความเข้ากันได้เป็นโมฆะ
Hubert Grzeskowiak

22

ความหมายทั้งสองรูปแบบเทียบเท่าใน Bash

จากหน้าคน:

ฟังก์ชันของ Shell มีการประกาศดังนี้:

name () compound-command [redirection]
function name [()] compound-command [redirection]

nameนี้กำหนดฟังก์ชั่นที่มีชื่อว่า ฟังก์ชันคำที่สงวนไว้เป็นทางเลือก หากมีการให้คำที่สงวนไว้ของฟังก์ชั่นวงเล็บจะเป็นตัวเลือก

แก้ไข: posixฉันเพิ่งสังเกตเห็นว่าคำถามนี้ถูกแท็ก ใน POSIX shที่functionคำหลักที่ไม่ได้ใช้ (แม้ว่ามันจะถูกสงวนไว้)


3

อีกหลายคนตอบถูกต้องแล้ว แต่นี่เป็นบทสรุปของฉัน:

รุ่นที่สองเป็นแบบพกพาและมีแนวโน้มที่จะทำงานกับเชลล์มาตรฐาน (โดยเฉพาะ POSIX) จำนวนมาก

รุ่นแรกจะใช้งานได้กับการทุบตีเท่านั้น แต่คุณอาจไม่ใส่วงเล็บตามด้วยชื่อฟังก์ชั่น

มิฉะนั้นจะแสดงถึงเอนทิตีที่เหมือนกันหลังจากทุบตีตีความมัน


ที่จริงแล้วเวอร์ชันแรกไม่สมเหตุสมผลยกเว้นการ จำกัด ว่ามันเป็นไวยากรณ์ที่ยอมรับได้สำหรับเชลล์บางตัว เมื่อคุณรวม() และfunctionคำหลักเปลือกทำงานเป็นถ้าคุณก็ไม่foo(){ ...; }อยู่แล้วยกเว้นของหลักสูตรสำหรับเปลือกในสิ่งที่มันเป็นไวยากรณ์ที่ไม่ถูกต้อง และคุณควรทำอย่างไรfunction foo { ...; }ถ้าคุณต้องการหรือfoo(){ ...; }อย่างอื่น
mikeserv
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.