ใน Bash เมื่อนามแฝงเมื่อสคริปต์และเมื่อใดที่จะเขียนฟังก์ชั่น?


360

ฉันใช้งาน Linux มาเกือบ 10 ปีแล้วเพื่อถามคำถามนี้ มันเป็นการทดลองและข้อผิดพลาดทั้งหมดและการท่องอินเทอร์เน็ตแบบดึกดื่น

แต่ผู้คนไม่ควรต้องการ 10 ปีสำหรับเรื่องนี้ ถ้าฉันเพิ่งเริ่มต้นด้วย Linux ฉันต้องการทราบว่าเมื่อใดจะใช้นามแฝงเวลาเขียนสคริปต์และเมื่อใดควรเขียนฟังก์ชัน

ฉันใช้นามแฝงสำหรับการดำเนินการที่ง่ายมากที่ไม่มีข้อโต้แย้ง

alias houston='cd /home/username/.scripts/'

ดูเหมือนว่าชัดเจน แต่บางคนทำสิ่งนี้:

alias command="bash bashscriptname"

(และเพิ่มลงใน.bashrcไฟล์)

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

เพราะนั่นคือสิ่งที่ฉันจะใส่บางสิ่งใน PATH ของฉันchmod +xซึ่งเป็นอีกสิ่งหนึ่งที่เกิดขึ้นหลังจากการทดลองและข้อผิดพลาดของ Linux มาหลายปี

ซึ่งนำฉันไปยังหัวข้อถัดไป ตัวอย่างเช่นฉันเพิ่มโฟลเดอร์ที่ซ่อนอยู่ ( .scripts/) ในโฮมไดเร็กตอรี่ไปที่ PATH ของฉันโดยเพิ่มบรรทัดลงใน.bashrc( PATH=$PATH:/home/username/.scripts/) ของฉันดังนั้นสิ่งใดที่ปฏิบัติการได้ในการเติมข้อความอัตโนมัติโดยอัตโนมัติ

ถ้าฉันต้องการ

ฉันไม่ต้องการสิ่งนั้นจริง ๆ ใช่ไหม? ฉันจะใช้เฉพาะสำหรับภาษาที่ไม่ใช่เปลือกเช่น Python

ถ้ามันเป็นเปลือกฉันก็สามารถเขียนฟังก์ชั่นภายในเดียวกันมาก.bashrc:

funcname () {
  somecommand -someARGS "$@"
}

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

แทนที่จะย้ายไดเรกทอรีทั้งหมดของสคริปต์จากคอมพิวเตอร์ไปยังคอมพิวเตอร์ฉันสิ้นสุดเพียงแค่แทนที่. bashrc ของคนอื่นด้วยตัวเองเนื่องจากพวกเขาไม่เคยทำการแก้ไขแม้แต่ครั้งเดียว

แต่ฉันคิดถึงอะไรเหรอ?

ดังนั้นสิ่งที่คุณจะบอกผู้ใช้ Linux เริ่มต้นเกี่ยวกับนามแฝงเมื่อสคริปต์และเมื่อใดที่จะเขียนฟังก์ชั่น?

หากไม่ชัดเจนฉันสมมติว่าคนที่ตอบคำถามนี้จะใช้ประโยชน์จากตัวเลือกทั้งสาม หากคุณใช้นามแฝงเท่านั้นหรือใช้สคริปต์เท่านั้นหรือใช้ฟังก์ชั่นเท่านั้นหรือหากคุณใช้นามแฝงและสคริปต์หรือชื่อแทนและฟังก์ชันหรือสคริปต์และฟังก์ชั่นเท่านั้น - คำถามนี้ไม่ได้มีจุดมุ่งหมายที่คุณจริงๆ


5
อาจเป็นไปได้ซ้ำกับฟังก์ชั่นทุบตีกับสคริปต์
Gilles

10
+1 สำหรับการระบุอย่างชัดเจนเซตย่อยทั้งหมดของ {alias, script, function} ที่คำถามนี้ไม่ได้มีจุดมุ่งหมาย +1 สำหรับความเชื่อแบบเด็ก ๆ ว่ามันโอเคสำหรับคุณที่จะไม่ใช้ชุดย่อยว่าง
Thomas L Holaday

1
แม้ว่าคำถามจะถามเกี่ยวกับการทุบตีโดยเฉพาะโปรดทราบว่าเชลล์ Bourne รุ่นเก่ามี 'นามแฝง' แต่ไม่สามารถใช้งานได้ สิ่งนี้อาจสร้างความแตกต่างหากคุณกังวลเกี่ยวกับความเข้ากันได้
AndyB

หาก. bashrc เป็นสถานที่ที่ดีที่สุดจริงๆหรืออย่างน้อยก็เป็นสถานที่ที่มั่นคงสำหรับสิ่งนี้? มีหลายวิธีที่จะทำสิ่งเดียวกันใน Linux ซึ่งฉันรู้สึกขอบคุณ แต่ทุกสิ่งเท่าเทียมกันฉันชอบทำสิ่งที่เหมือนกันมากที่สุด
Kit10

คำตอบ:


242

นามแฝงไม่ควรมีประสิทธิภาพ (โดยทั่วไป) มากกว่าเปลี่ยนตัวเลือกเริ่มต้นของคำสั่ง มันไม่มีอะไรมากไปกว่าการเปลี่ยนข้อความธรรมดาในชื่อคำสั่ง มันไม่สามารถทำอะไรกับอาร์กิวเมนต์ได้ แต่ส่งผ่านไปยังคำสั่งที่มันทำงานจริง ดังนั้นหากคุณต้องการเพิ่มอาร์กิวเมนต์ที่ด้านหน้าคำสั่งเดียวนามแฝงจะทำงาน ตัวอย่างทั่วไปคือ

# Make ls output in color by default.
alias ls="ls --color=auto"
# make mv ask before overwriting a file by default
alias mv="mv -i"

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

grep() { 
    if [[ -t 1 ]]; then 
        command grep -n "$@"
    else 
        command grep "$@"
    fi
}

มันเป็นตัวอย่างที่สมบูรณ์แบบของฟังก์ชั่นเพราะมันซับซ้อนเกินไปสำหรับนามแฝง (ต้องการค่าเริ่มต้นที่แตกต่างกันตามเงื่อนไข) แต่ไม่ใช่สิ่งที่คุณต้องการในสคริปต์แบบไม่โต้ตอบ

หากคุณมีฟังก์ชั่นหรือฟังก์ชั่นใหญ่เกินไปให้ใส่เข้าไปในไฟล์ที่แยกกันในไดเรกทอรีที่ซ่อนอยู่และส่งไปที่~/.bashrc:

if [ -d ~/.bash_functions ]; then
    for file in ~/.bash_functions/*; do
        . "$file"
    done
fi

สคริปต์ควรยืนอยู่บนตัวของมันเอง ควรมีค่าเป็นสิ่งที่สามารถนำกลับมาใช้ใหม่หรือใช้เพื่อวัตถุประสงค์มากกว่าหนึ่ง


14
สิ่งสำคัญคือต้องจำไว้ว่า - หากไม่ได้แหล่งที่มา.หรือsource- สคริปต์จะถูกดำเนินการโดยกระบวนการทุบตีแยกต่างหากและมีสภาพแวดล้อมของตัวเอง ด้วยเหตุผลนี้สิ่งใดก็ตามที่ปรับเปลี่ยนสภาพแวดล้อมของเชลล์ (เช่นฟังก์ชั่นตัวแปร ฯลฯ ) จะไม่คงอยู่ในสภาพแวดล้อมของเชลล์ที่คุณเรียกใช้สคริปต์
Will Vousden

260

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

นามแฝงและฟังก์ชั่น¹

  • เนื้อหาทั้งหมดของนามแฝงและฟังก์ชั่นจะถูกเก็บไว้ในหน่วยความจำของเชลล์
  • เป็นผลมาจากธรรมชาติของที่นี่คือนามแฝงและฟังก์ชั่นสามารถเพียง แต่นำมาใช้โดยเปลือกปัจจุบันและไม่โดยโปรแกรมอื่น ๆ ที่คุณอาจจะเรียกจากเปลือกเช่นแก้ไขข้อความสคริปต์หรือแม้กระทั่งกรณีเด็กของเปลือกเดียวกัน
  • นามแฝงและฟังก์ชั่นถูกดำเนินการโดยเชลล์ปัจจุบันนั่นคือมันทำงานภายในและส่งผลกระทบต่อสภาพแวดล้อมปัจจุบันของเชลล์²ไม่จำเป็นต้องมีกระบวนการแยกต่างหากในการเรียกใช้นามแฝงหรือฟังก์ชัน

สคริป

  • เชลล์ไม่เก็บสคริปต์ไว้ในหน่วยความจำ สคริปต์จะถูกอ่านจากไฟล์ที่จัดเก็บทุกครั้งที่ต้องการ หากพบสคริปต์จากการ$PATHค้นหาเชลล์จำนวนมากจะเก็บแฮชของชื่อพา ธ ไว้ในหน่วยความจำเพื่อประหยัดเวลาในการ$PATHค้นหาในอนาคตแต่นั่นเป็นขอบเขตของรอยเท้าหน่วยความจำของสคริปต์เมื่อไม่ได้ใช้งาน
  • สคริปต์สามารถเรียกใช้งานได้หลากหลายวิธีมากกว่าฟังก์ชั่นและนามแฝง พวกมันสามารถถูกส่งผ่านเป็นอาร์กิวเมนต์ไปยังล่ามเช่นsh scriptหรือเรียกใช้โดยตรงในฐานะที่ปฏิบัติการได้ซึ่งในกรณีนี้ล่ามในบรรทัด shebang (เช่น#!/bin/sh) จะถูกเรียกให้ทำงาน ในทั้งสองกรณีสคริปต์จะดำเนินการโดยกระบวนการล่ามที่แยกต่างหากโดยมีสภาพแวดล้อมของตัวเองแยกออกจากเชลล์ของคุณซึ่งสภาพแวดล้อมของสคริปต์จะไม่ส่งผลกระทบ แต่อย่างใด ที่จริงแล้วล่ามเชลล์ไม่จำเป็นต้องจับคู่เชลล์ที่เรียกใช้ เนื่องจากสคริปต์ที่เรียกใช้ด้วยวิธีนี้ดูเหมือนจะทำงานเหมือนกับโปรแกรมทั่วไปใด ๆ โปรแกรมเหล่านี้จึงสามารถใช้งานได้

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

ใบสมัคร

จากที่กล่าวมาข้างต้นเราสามารถหาแนวทางทั่วไปว่าจะสร้างสคริปต์หรือฟังก์ชั่น / นามแฝง

  • โปรแกรมอื่น ๆ นอกเหนือจากเชลล์ของคุณจำเป็นต้องใช้งานหรือไม่? ถ้าเป็นเช่นนั้นจะต้องมีสคริปต์

  • คุณต้องการให้มันพร้อมใช้งานจากเชลล์แบบโต้ตอบหรือไม่? เป็นเรื่องปกติที่จะต้องการเปลี่ยนพฤติกรรมเริ่มต้นของคำสั่งจำนวนมากเมื่อทำงานแบบโต้ตอบโดยไม่กระทบกับคำสั่ง / สคริปต์ภายนอก สำหรับกรณีนี้ให้ใช้นามแฝง / ฟังก์ชั่นตั้งค่าในไฟล์ rc "แบบโต้ตอบโหมดเท่านั้น" ของเชลล์ (สำหรับbashกรณีนี้.bashrc)

  • จำเป็นต้องเปลี่ยนสภาพแวดล้อมของเชลล์หรือไม่? ฟังก์ชั่น / นามแฝงหรือสคริปต์ที่มาเป็นตัวเลือกที่เป็นไปได้

  • คุณใช้บ่อยไหม อาจมีประสิทธิภาพมากกว่าในการเก็บไว้ในหน่วยความจำดังนั้นให้เป็นฟังก์ชัน / นามแฝงถ้าเป็นไปได้

  • ในทางกลับกันมันเป็นสิ่งที่คุณใช้งานน้อยมาก? ในกรณีนี้คุณไม่จำเป็นต้องใช้หน่วยความจำแบบหมูเมื่อคุณไม่ต้องการใช้งานดังนั้นจึงเป็นสคริปต์


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

²ทุกกระบวนการทำงานในระบบ Unix มีสภาพแวดล้อมที่ประกอบด้วยvariable=valueคู่ที่มักจะมีการตั้งค่าระดับโลกเช่นLANGสถานที่เริ่มต้นและPATHการระบุเส้นทางการค้นหาปฏิบัติการ


26
IMHO นี่คือคำตอบที่ดีที่สุด
Luc M

4
รูปแบบคำถาม / คำตอบเป็นความคิดที่ดี ฉันอาจขโมยสิ่งนั้น ;-)
มิเคล

4
น่าสังเกตว่า: หากสคริปต์สองตัว (หรือมากกว่า) จำเป็นต้องแชร์รหัสบางอย่างอาจเป็นการดีที่สุดที่จะวางโค้ดนั้นลงในฟังก์ชั่นซึ่งอยู่ในไฟล์ที่สามซึ่งสคริปต์ทั้งสองนั้นนำเข้า / แหล่งที่มา
kbolino

3
รายการอื่นที่จะเพิ่มลงในรายการคำถาม: คุณจำเป็นต้องเปลี่ยนฟังก์ชันการทำงานในคำสั่งทันทีหรือไม่ การเปลี่ยนแปลงสคริปต์จะปรากฏในทุกเซสชันในขณะที่ฟังก์ชั่นและชื่อแทนจะต้องโหลดใหม่หรือกำหนดใหม่ในแต่ละเซสชั่น
Stratus3D

3
คำตอบที่ดี. สิ่งสำคัญอีกประการหนึ่ง (สำหรับฉัน): เมื่อสร้าง "ทางลัด" ไปยังอุปกรณ์อื่น ๆ ควรใช้นามแฝงเนื่องจากการเติมข้อความอัตโนมัติที่มีอยู่จะใช้งานได้กับนามแฝงเท่านั้น แต่ไม่ใช่กับสคริปต์หรือฟังก์ชัน (เช่น +1 สำหรับนามแฝง) เช่นโดยการสร้างalias g='gradle'ฉันได้รับการเติมข้อความอัตโนมัติ gradle เมื่อใช้gนามแฝงของฉันแต่จะไม่ได้รับมันออกจากกล่องเมื่อใช้สคริปต์กับgradle $*หรือฟังก์ชั่นที่มีgradle $@
Yoav Aharoni

37

ฉันคิดว่ามันขึ้นอยู่กับรสนิยมของแต่ละคน สำหรับฉันตรรกะไปเช่นนี้:

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

มีจริงๆไม่มีอะไรที่จะ จำกัด คุณจากการทำสิ่งที่ทำงาน


6
ฉันมักจะข้ามตัวเลือกฟังก์ชั่นและสคริปต์ทันที แต่ฉันยอมรับว่ามันเป็นเรื่องของรสนิยมส่วนหนึ่ง
แบร์นฮาร์ด

2
ฟังก์ชั่นเริ่มต้นที่เหมาะสมถ้าคุณต้องการในหลาย ๆ สคริปต์
นิลส์

7
... หรือหากคุณต้องการผลข้างเคียงเพื่อเปลี่ยนเชลล์ปัจจุบัน
เกล็นแจ็คแมน

ทำให้รู้สึกมนุษย์!
นักสำรวจ

15

อย่างน้อยบางส่วนมันเป็นเรื่องของรสนิยมส่วนตัว ในทางตรงกันข้ามมีความแตกต่างการทำงานที่ชัดเจน:

  • นามแฝง: เหมาะสำหรับการแทนที่ข้อความอย่างง่ายเท่านั้นไม่มีข้อโต้แย้ง / พารามิเตอร์
  • ฟังก์ชั่น: ง่ายต่อการเขียน / ใช้งานความสามารถในการเขียนสคริปต์เต็มเชลล์ใช้ได้เฉพาะในทุบตี
  • สคริปต์: มากกว่าหรือน้อยกว่าเช่นฟังก์ชั่น แต่สามารถใช้ได้ (เรียกได้) นอกทุบตีเช่นกัน

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

PS: สำหรับalias command="bash bashscriptname"ฉันจริง ๆ ไม่เห็นเหตุผลที่จะทำเช่นนี้ แม้ว่าbashscriptnameจะไม่ได้อยู่ใน $ PATH แบบง่าย ๆalias c=/path/to/scriptก็เพียงพอแล้ว


1
ในalias command="bash bashscriptname"สคริปต์ไม่จำเป็นต้องสามารถเรียกใช้งานได้ ในalias c=/path/to/scriptนั้นจะต้องมี
Martin - - ーマン

มันไม่เป็นความจริงเลยในทุกฟังก์ชั่นที่ว่า "ใช้ได้เฉพาะภายใน Bash" หากคุณหมายถึงว่าพวกเขาเป็นคุณสมบัติ Bash-only นั่นเป็นเพียงเท็จ (บอร์นเชลล์และอนุพันธ์ที่เข้ากันได้ทุกตัวมี) และถ้าคุณหมายถึงว่าพวกเขาเป็นคุณลักษณะของเปลือกโต้ตอบที่ไม่ถูกต้องทั้ง (นามแฝงตัวแปรและฟังก์ชั่นที่กำหนดไว้ในไฟล์ที่ได้รับการโหลดเมื่อเริ่มต้นโดยเปลือกโต้ตอบแน่นอนจะไม่ถูกโหลดโดยเปลือกไม่โต้ตอบ)
tripleee

@tripleee ความหมายเหมือน "คุณไม่สามารถexec()ใช้ฟังก์ชั่นของเชลล์" :-)
nohillside

11

นี่คือบางจุดเพิ่มเติมเกี่ยวกับนามแฝงและฟังก์ชัน:

  • นามแฝงและฟังก์ชันชื่อเดียวกันสามารถอยู่ร่วมกันได้
  • alias namespace ถูกค้นหาก่อน (ดูตัวอย่างแรก)
  • นามแฝงไม่สามารถตั้งค่า (un) ใน subshells หรือสภาพแวดล้อมแบบไม่โต้ตอบ (ดูตัวอย่างที่สอง)

ตัวอย่างเช่น:

alias f='echo Alias'; f             # prints "Alias"
function f { echo 'Function'; }; f  # prints "Alias"
unalias f; f                        # prints "Function"

อย่างที่เราเห็นมีเนมสเปซแยกต่างหากสำหรับชื่อแทนและฟังก์ชั่น รายละเอียดเพิ่มเติมสามารถพบได้โดยใช้declare -A -p BASH_ALIASESและdeclare -f fซึ่งพิมพ์คำจำกัดความของพวกเขา (ทั้งสองจะถูกเก็บไว้ในหน่วยความจำ)

ตัวอย่างที่แสดงข้อ จำกัด ของนามแฝง:

alias a='echo Alias'
a        # OK: prints "Alias"
eval a;  # OK: prints "Alias"
( alias a="Nested"; a );  # prints "Alias" (not "Nested")
( unalias a; a );         # prints "Alias"
bash -c "alias aa='Another Alias'; aa"  # ERROR: bash: aa: command not found

ในขณะที่เราสามารถเห็นนามแฝงไม่สามารถซ้อนกันได้ซึ่งแตกต่างจากฟังก์ชั่น นอกจากนี้การใช้งานของพวกเขาจะถูก จำกัด ในช่วงการโต้ตอบ

สุดท้ายทราบว่าคุณสามารถมีการคำนวณโดยพลการในนามแฝงโดยการประกาศฟังก์ชั่นและเรียกมันทันทีเช่น:

alias a_complex_thing='f() { do_stuff_in_function; } f'

ซึ่งมีการใช้กันอย่างแพร่หลายในกรณีของนามแฝง Git ประโยชน์ของการทำเช่นนี้มากกว่าการประกาศฟังก์ชั่นคือนามแฝงของคุณไม่สามารถเขียนทับโดย.สคริปต์ที่มา (หรือใช้) สคริปต์ซึ่งเกิดขึ้นเพื่อประกาศฟังก์ชันที่มีชื่อเดียวกัน


10

เมื่อใดควรเขียนสคริปต์ ...

  • สคริปต์รวบรวมส่วนประกอบซอฟต์แวร์ (เครื่องมือ, คำสั่ง, กระบวนการ, โปรแกรมปฏิบัติการ) ลงในส่วนประกอบที่ซับซ้อนมากขึ้นซึ่งตัวเองอาจรวมตัวกันเป็นส่วนประกอบที่ซับซ้อนมากขึ้น
  • สคริปต์มักจะทำให้สามารถเรียกใช้งานได้ดังนั้นจึงสามารถเรียกได้โดยใช้ชื่อ เมื่อเรียกว่า subprocess ใหม่จะเกิดขึ้นเพื่อให้สคริปต์ทำงานระบบคัดลอกexportตัวแปร ed และ / หรือฟังก์ชั่นใด ๆจะถูกส่งผ่านโดยค่าไปยังสคริปต์ การเปลี่ยนแปลงตัวแปรเหล่านั้นจะไม่แพร่กระจายกลับไปยังสคริปต์หลัก
  • สคริปต์อาจถูกโหลด (ที่มา) ราวกับว่าพวกเขาเป็นส่วนหนึ่งของสคริปต์การโทร สิ่งนี้คล้ายกับที่ภาษาอื่นเรียกว่า "นำเข้า" หรือ "รวม" เมื่อแหล่งที่มาพวกเขาดำเนินการภายในกระบวนการที่มีอยู่ ไม่มีกระบวนการย่อยวางไข่

เมื่อใดจึงจะเขียนฟังก์ชั่น ...

  • ฟังก์ชันเป็นเชลล์สคริปต์ที่โหลดล่วงหน้าอย่างมีประสิทธิภาพ พวกเขาทำงานได้ดีกว่าการเรียกสคริปต์แยกต่างหาก แต่ต้องอ่านจากดิสก์เชิงกลเท่านั้น การเพิ่มขึ้นอย่างมากมายของ flashdrives, SSD และการแคชปกติใน RAM ที่ไม่ได้ใช้ทำให้การปรับปรุงนั้นไม่สามารถวัดได้อย่างมากมาย
  • ฟังก์ชั่นทำหน้าที่เป็นหลักการของทุบตีหมายถึงการบรรลุ modularity, การห่อหุ้มและนำมาใช้ใหม่ พวกเขาปรับปรุงความชัดเจนความน่าเชื่อถือและการบำรุงรักษาของสคริปต์
  • กฎไวยากรณ์สำหรับการเรียกใช้ฟังก์ชันจะเหมือนกับกฎการเรียกใช้ไฟล์ที่เรียกทำงานได้ ฟังก์ชั่นที่มีชื่อเดียวกันกับไฟล์ที่เรียกทำงานได้จะถูกเรียกใช้แทนไฟล์ที่เรียกทำงานได้
  • ฟังก์ชั่นเป็นท้องถิ่นของสคริปต์ที่พวกเขาอยู่
  • ฟังก์ชั่นอาจถูกส่งออก ( คัดลอกโดยค่า ) เพื่อให้สามารถใช้ภายในเรียกว่าสคริปต์ ดังนั้นฟังก์ชั่นเพียงเผยแพร่ไปยังกระบวนการเด็กไม่พ่อแม่
  • ฟังก์ชั่นสร้างคำสั่งที่นำกลับมาใช้ใหม่ได้ซึ่งมักจะรวมอยู่ในไลบรารี (สคริปต์ที่มีนิยามฟังก์ชันเท่านั้น) เพื่อให้ได้มาจากสคริปต์อื่น

เมื่อใดจึงจะเขียนนามแฝง ...

ภายในสคริปต์เช่นสคริปต์ไลบรารีบางครั้งจำเป็นต้องใช้นามแฝงสำหรับฟังก์ชันเช่นเมื่อเปลี่ยนชื่อฟังก์ชัน แต่จำเป็นต้องใช้ความเข้ากันได้แบบย้อนหลัง สามารถทำได้โดยการสร้างฟังก์ชั่นที่เรียบง่ายด้วยชื่อเก่าที่ผ่านการขัดแย้งทั้งหมดของมันไปยังฟังก์ชั่นใหม่ ...

# A bash in-script 'alias'
function oldFunction () { newFunction "$@"; }

9

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

ซึ่งอาจจะมีความสำคัญสำหรับการทำงาน - ฟังก์ชั่นได้เร็วขึ้นเพราะมันไม่ได้และfork() exec()ในสถานการณ์ปกติความแตกต่างนั้นเล็กน้อย แต่ถ้าคุณทำการดีบั๊กระบบที่มีหน่วยความจำไม่เพียงพอและมีการฟาดหน้ามันอาจทำให้เกิดความแตกต่างได้

นอกจากนี้หากคุณต้องการแก้ไขสภาพแวดล้อมเชลล์ปัจจุบันของคุณคุณควรใช้ฟังก์ชั่น ยกตัวอย่างเช่นฟังก์ชั่นการค้นหาอาจมีการเปลี่ยนแปลงคำสั่ง$PATHสำหรับเปลือกปัจจุบัน แต่สคริปต์ไม่สามารถเพราะมันทำงานกับสำเนาส้อม / exec $PATHของ


การถ่ายทอดฟังก์ชั่นนี้ให้กับเด็ก ๆ ทำงานอย่างไร
HappyFace

1
@HappyFace ใน Bash คุณสามารถexport -fใช้งานได้แม้ว่าการทำงานภายในที่แม่นยำของสิ่งนี้จะค่อนข้างคลุมเครือ นี่ไม่ใช่พกพาไปสู่ ​​Bourne shell แบบดั้งเดิมที่ฉันเชื่อ
tripleee

7

สคริปต์และนามแฝงและสคริปต์และฟังก์ชั่นไม่ได้เกิดขึ้นพร้อมกัน คุณสามารถและเก็บชื่อแทนและฟังก์ชั่นในสคริปต์

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

เนื่องจากชื่อแทนไม่ได้รับการแปรพารามิเตอร์พวกเขาจะถูก จำกัด มาก; มักจะกำหนดพารามิเตอร์เริ่มต้นบางอย่าง

ฟังก์ชั่นเป็นหน่วยเฉพาะกิจการของรหัสแนวคิดที่ดีที่กำหนดของไม่กี่บรรทัดของรหัสที่ไม่สามารถแยกออกเป็นขนาดเล็กชิ้นส่วนที่มีประโยชน์; หนึ่งซึ่งสามารถนำมาใช้โดยตรงหรืออื่น ๆ โดยฟังก์ชั่นอื่น ๆ


5

หากควรเร็วมากให้ตั้งชื่อแทนหรือฟังก์ชัน

ถ้ามันควรจะใช้งานได้นอกเปลือกที่คุณต้องการทำให้มันเป็นสคริปต์ 1

หากมีการโต้แย้งให้มันเป็นฟังก์ชั่นหรือสคริปต์

หากต้องการมีอักขระพิเศษให้ตั้งชื่อแทนหรือสคริปต์ 2

หากต้องการทำงานกับ sudo ให้ตั้งชื่อแทนหรือสคริปต์ 3

หากคุณต้องการเปลี่ยนได้อย่างง่ายดายโดยไม่ต้องออกจากระบบสคริปต์จะง่ายขึ้น 4

เชิงอรรถ

1หรือทำให้เป็นนามแฝงใส่ไว้ใน~/.envและตั้งค่าexport ENV="$HOME/.env"แต่มันซับซ้อนที่จะทำให้มันทำงานได้อย่างสะดวก

2ชื่อฟังก์ชั่นจะต้องเป็นตัวระบุดังนั้นพวกเขาจะต้องเริ่มต้นด้วยตัวอักษรและอาจประกอบด้วยตัวอักษรตัวเลขและขีดล่างเท่านั้น alias +='pushd +1'ตัวอย่างเช่นผมมีนามแฝง มันไม่สามารถเป็นฟังก์ชั่นได้

3alias sudo='sudo 'และเพิ่มนามแฝง Ditto คำสั่งอื่น ๆ เช่นstrace, gdbฯลฯ ที่จะนำคำสั่งเป็นอาร์กิวเมนต์แรกของ

4ดูเพิ่มเติม: fpath แน่นอนคุณสามารถทำได้source ~/.bashrcหรือคล้ายกัน แต่มักจะมีผลข้างเคียงอื่น ๆ


1
ฉันไม่รู้ว่าคุณสามารถใช้นามแฝง+ในการทุบตีได้ ที่น่าสนใจหลังจากการทดสอบฉันพบว่าในทุบตีคุณสามารถสร้าง+นามแฝง แต่ไม่ใช่ฟังก์ชั่นอย่างที่คุณพูด แต่ zsh เป็นสิ่งที่ตรงกันข้าม - +อาจเป็นฟังก์ชั่น แต่ไม่ใช่นามแฝง
เควิน

ในคุณต้องเขียนzsh alias -- +='some command here'
Mikel

อย่างใดฉันไม่คิดว่านามแฝง+เป็นแบบพกพา ดูข้อมูลจำเพาะ POSIX ในชื่อนามแฝง
jw013

3
โหวตขึ้นสำหรับครอบคลุมsudoการใช้งาน เกี่ยวกับเชิงอรรถ 4 ฉันเก็บชื่อแทนของฉันไว้ใน~/.bash_aliasesคำจำกัดความของฟังก์ชันและ~/.bash_functionsเพื่อให้ฉันสามารถนำกลับมาใช้ได้อย่างง่ายดายsource(โดยไม่มีอันตรายจากผลข้างเคียง)
Anthony Geoghegan

3

เพียงเพิ่มบันทึกย่อ:

  • สคริปต์ที่แยกต่างหากเท่านั้นที่สามารถใช้กับ sudo (เช่นถ้าคุณต้องการแก้ไขไฟล์ระบบ) ตัวอย่างเช่น:
sudo v /etc/rc.conf  #where v runs vim in a new terminal window;
  • นามแฝงหรือฟังก์ชั่นเท่านั้นที่สามารถแทนที่คำสั่งระบบภายใต้ชื่อเดียวกัน (สมมติว่าคุณเพิ่มสคริปต์ของคุณไปยังจุดสิ้นสุดของ PATH ซึ่งฉันคิดว่าเหมาะสมสำหรับความปลอดภัยในกรณีที่มีการสร้างสคริปต์โดยไม่ตั้งใจหรือร้ายกาจ ตัวอย่างเช่น:
alias ls='ls --color=auto'  #enable colored output;
  • นามแฝงและฟังก์ชั่นใช้หน่วยความจำและเวลาน้อยลงสำหรับการดำเนินการ แต่ใช้เวลาในการโหลด (เนื่องจากเชลล์จะต้องตีความพวกเขาทั้งหมดก่อนที่จะแสดงพร้อมท์ให้คุณ) พิจารณาสิ่งนี้หากคุณใช้กระบวนการเชลล์ใหม่เป็นประจำตัวอย่างเช่น
# pressing key to open new terminal
# waiting for a few seconds before shell prompt finally appears.

นอกเหนือจากนั้นคุณสามารถใช้รูปแบบที่ง่ายที่สุดที่เป็นไปได้เช่นพิจารณาชื่อแทนจากนั้นจึงใช้งานจากนั้นใช้สคริปต์


5
sudoนามแฝงนอกจากนี้ยังสามารถนำมาใช้กับ alias sudo='sudo 'แต่ก่อนที่คุณต้องการ
มิเคล

ในขณะที่มันเป็นความจริงที่การเรียกใช้งานสคริปต์จะใช้หน่วยความจำเพิ่มขึ้นในช่วงเวลาของ fork + exec ชั่วขณะการมีโค้ดจำนวนมากโหลดเข้าไปในหน่วยความจำของเชลล์ปัจจุบันเช่นทำให้หน่วยความจำมากขึ้นในอนาคต ได้รับใช้ค่อนข้างน้อย
tripleee

2

กฎง่ายๆของฉันคือ:

  • นามแฝง - คำสั่งเดียวไม่มีพารามิเตอร์
  • ฟังก์ชั่น - หนึ่งคำสั่งพารามิเตอร์บางอย่าง
  • สคริปต์ - หลายคำสั่งไม่มีพารามิเตอร์

1

ในสภาพแวดล้อมที่มีผู้ใช้หลายคน (หรือหลายระบบ) ฉันใช้สคริปต์สำหรับทุกสิ่งแม้ว่ามันจะกลายเป็นเสื้อคลุมสั้น ๆ 'exec something .... '

แน่นอนว่ามันช้าลงในทางเทคนิค / มีประสิทธิภาพน้อยกว่านามแฝงหรือฟังก์ชั่น แต่แทบจะไม่สำคัญเลย - และถ้ามันอยู่ในพา ธ สคริปต์จะทำงานได้เสมอ

คุณสมบัติของคุณอาจถูกเรียกจาก cron จากบางสิ่งที่มีสภาพแวดล้อมที่ลดลงหรือปรับเปลี่ยนเช่น sudo หรือ env หรือผู้ใช้อาจใช้เชลล์ที่แตกต่างกับคุณ - ทั้งหมดหรือที่มีแนวโน้มที่จะทำลายนามแฝงหรือฟังก์ชั่น

หากคุณมีบางสิ่งที่อ่อนไหวด้านประสิทธิภาพจัดการกับกรณีพิเศษหรือดีกว่าพิจารณาว่าทริกเกอร์ในการเขียนในภาษาสคริปต์ที่ใช้งานได้มากกว่า

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

T


0

ตัวอย่างสำหรับสถานการณ์ที่คุณต้องการใช้นามแฝง

ฉันรู้ว่านี่เป็นโพสต์เก่า แต่ฉันต้องการที่จะชี้ให้เห็นสถานการณ์ที่ฉันเกือบจะต้องใช้การรวมกันของชื่อแทนกับสคริปต์และฉันเลือกที่จะไม่ใช้ฟังก์ชั่น

ฉันมีสคริปต์ที่~/.bin/เรียกsetupว่าทำสิ่งต่อไปนี้: 1

  1. พาฉันไปยังไดเรกทอรีที่แน่นอน
  2. กำหนดตัวแปรบางอย่าง
  3. พิมพ์ข้อความเกี่ยวกับสถานะของไดเรกทอรี 2

ประเด็นก็คือถ้าฉันจะเรียกใช้setup <project-name>ฉันจะไม่ได้กำหนดตัวแปรเหล่านั้นและฉันจะไม่ได้ไปยังไดเรกทอรีเลย วิธีการแก้ปัญหาที่ผมพบว่าเป็นที่ที่ดีที่สุดคือการเพิ่มสคริปต์นี้PATHและเพิ่มalias setup=". ~/.bin/setup"การ~/.bashrcหรืออะไรก็ตาม

หมายเหตุ:

  1. ฉันใช้สคริปต์สำหรับงานนี้ไม่ใช่ฟังก์ชั่นไม่ใช่เพราะมันยาวเป็นพิเศษ แต่เพราะฉันสามารถแก้ไขได้และฉันไม่ต้องแหล่งไฟล์หลังจากแก้ไขถ้าฉันต้องการรีเฟรชการใช้งาน
  2. กรณีที่คล้ายกันนี้เป็นของฉันเมื่อฉันสร้างสคริปต์เพื่อโหลดดอทไฟล์ทั้งหมด 3
  1. สคริปต์ที่มีให้บริการในdotfiles ของฉันพื้นที่เก็บข้อมูล.bin/ภายใต้
  2. เกี่ยวกับสคริปต์ : ฉันให้อาร์กิวเมนต์นี้ซึ่งเป็นชื่อสำหรับโครงการที่ฉันกำหนดไว้ในระดับสูง หลังจากนั้นสคริปต์ก็จะพาฉันไปยังไดเรกทอรีที่ถูกต้องตามcsvไฟล์ที่กำหนด ตัวแปรที่กำหนดถูกนำมาจาก makefile ในไดเรกทอรีนั้น สคริปต์จะทำงานหลังจากนั้นls -lและgit statusแสดงให้ฉันเห็นว่าเกิดอะไรขึ้นที่นั่น
  3. สคริปต์นั้นมีอยู่ในที่เก็บ dotfiles ของฉัน.bin/ด้วยเช่นกัน

1
อืมดูเหมือนว่ามันควรจะเป็นฟังก์ชั่นไม่ใช่ชุดของสมนามสคริปต์ (BTW ระบบจะสะกดคำว่า "environment" ไม่ใช่ "enviorments")
Wildcard

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

0

เมื่อใดที่จะเขียนสคริปต์

เมื่อคุณอาจต้องการเรียกใช้คำสั่งจากเครื่องมืออื่นที่ไม่ใช่เชลล์

ซึ่งรวมถึงเสียงเรียกเข้า (สำหรับฉัน): การเขียนตัวกรองและโปรแกรมอื่น ๆ เป็นสคริปต์ฉันสามารถทำสิ่งที่ต้องการ:%!my-filterกรองไฟล์ผ่านโปรแกรมจากโปรแกรมแก้ไขของฉัน

ถ้าmy-filterเป็นหน้าที่หรือนามแฝงนั่นคงเป็นไปไม่ได้

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