วิธีต่างๆในการเรียกใช้งานเชลล์สคริปต์


44

มีหลายวิธีในการรันสคริปต์สิ่งที่ฉันรู้คือ:

/path/to/script # using the path (absolute or relative)
. script        # using the . (dot)
source script   # using the `source` command

เป็นมากกว่านี้หรือไม่ ความแตกต่างระหว่างพวกเขาคืออะไร? มีสถานการณ์ที่ฉันต้องใช้อย่างอื่นหรือไม่?


ดีใจที่ได้รู้ต้องขอบคุณคำถามและคำตอบของคุณด้านล่างโดยเฉพาะ Shawn ฉันต้องการเพิ่มบางสิ่งที่ไม่ชัดเจนสำหรับฉันจนกว่าฉันจะทำการทดสอบสองสามครั้ง การรวม "/" ในวิธีที่สองด้านบนจะใช้คำสั่งไปที่โหมด 1 ด้านบน นั่นคือในขณะที่ "./myscript.sh" ตามโหมด 1, ". myscript.sh" ยึดติดกับโหมด 2 คุณพูดถึง "ใช้เส้นทาง (สัมบูรณ์หรือสัมพัทธ์)" แต่ต้องการให้ชัดเจน
อรุณ

คำตอบ:


32

อีกวิธีหนึ่งคือการเรียกล่ามและส่งผ่านเส้นทางไปยังสคริปต์ไปที่:

/bin/sh /path/to/script

จุดและแหล่งที่มานั้นเทียบเท่ากัน (แก้ไข: ไม่พวกเขาไม่ได้: ในฐานะที่ KeithB ชี้ให้เห็นในความคิดเห็นเกี่ยวกับคำตอบอื่น "." ทำงานเฉพาะในเชลล์ที่เกี่ยวข้องกับ bash โดยที่ "ซอร์ส" ทำงานได้ทั้งเชลล์ bash และ csh ที่เกี่ยวข้อง) มันรันสคริปต์ใน -place (ราวกับว่าคุณคัดลอกและวางสคริปต์ไว้ตรงนั้น) ซึ่งหมายความว่ายังคงมีฟังก์ชั่นและตัวแปรอื่น ๆ นอกจากนี้ยังหมายความว่าหากสคริปต์ทำซีดีลงในไดเรกทอรีคุณจะยังอยู่ที่นั่นเมื่อดำเนินการเสร็จ

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

/ path / to / script และสคริปต์ / bin / sh แตกต่างกันเล็กน้อย โดยทั่วไปสคริปต์จะมี "shebang" ในตอนต้นที่มีลักษณะดังนี้:

#! /bin/bash

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

ตัวอย่างเช่นสคริปต์ Perl และสคริปต์ Ruby เริ่มต้นด้วย (ตามลำดับ):

#! /bin/perl

และ

#! /bin/ruby

หากคุณเรียกใช้หนึ่งในสคริปต์เหล่านั้นโดยการเรียกใช้/bin/sh scriptแล้วจะไม่ทำงานเลย

จริง ๆ แล้ว Ubuntu ไม่ได้ใช้ bash shell แต่เป็นอันที่คล้ายกันมากที่เรียกว่า dash สคริปต์ที่ต้องใช้ทุบตีอาจทำงานผิดพลาดเล็กน้อยเมื่อถูกเรียกโดยทำ/bin/sh scriptเพราะคุณเพิ่งเรียกสคริปต์ทุบตีโดยใช้ล่ามประ

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

อีกรูปแบบย่อย: คุณสามารถนำหน้าวิธีใด ๆ เหล่านี้เพื่อรันสคริปต์ด้วย eval ดังนั้นคุณสามารถมี

eval sh script
eval script
eval . script

และอื่น ๆ มันไม่ได้เปลี่ยนแปลงอะไรเลย แต่ฉันคิดว่าฉันจะใส่ไว้เพื่อความทั่วถึง


6
การพูดว่า "Ubuntu จริง ๆ แล้วไม่ใช้ bash shell" นั้นไม่แม่นยำและไม่ถูกต้องทางเทคนิค อูบุนตูจะใช้เปลือกทุบตี, ประเด็นก็คือว่าshสอดคล้องกับการแต่ไม่ถึงdash bash
Faheem Mitha

@Shawn ใน firts para คุณเขียนว่า "มันรันสคริปต์แบบแทนที่ (ราวกับว่าคุณคัดลอกและวางสคริปต์ไว้ตรงนั้น) ซึ่งหมายความว่ายังคงมีฟังก์ชั่นและตัวแปรอื่น ๆ ที่ไม่ใช่โลคอลในสคริปต์" บรรทัดที่สองคุณหมายถึงอะไร คุณช่วยอธิบายได้ไหม
Geek

@Geek เมื่อคุณรันสคริปต์เป็นกระบวนการลูก (วิธีปกติ) ตัวแปรและฟังก์ชั่นใด ๆ ที่กำหนด (เป็นสภาพแวดล้อม) หายไปเมื่อกระบวนการสิ้นสุดลง หากคุณจัดหาสคริปต์ตัวแปรและฟังก์ชันเหล่านั้นจะถูกสร้างขึ้นในสภาพแวดล้อมปัจจุบัน เมื่อสคริปต์เสร็จสิ้นการเปลี่ยนแปลงของสภาพแวดล้อมจะยังคงอยู่
Shawn J. Goff

@ ShawnJ.Goff ขอบคุณสำหรับการชี้แจง +1
Geek

เรื่องย่อ: Bourne Shell (sh) ยอมรับเฉพาะจุดเท่านั้นไม่ใช่แหล่งที่มาเนื่องจากเป็น bash-builtin pubs.opengroup.org/onlinepubs/9699919799/utilities/… ดังนั้นฉันจะบอกว่าวิธีที่พกพาได้มากที่สุดคือจุด
dezza

9

คนส่วนใหญ่ทำการดีบักเชลล์สคริปต์โดยการเพิ่มแฟล็กการดีบักต่อไปนี้ลงในสคริปต์:

set -x     # Print command traces before executing command.
set -v     # Prints shell input lines as they are read.
set -xv    # Or do both

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

แทนที่จะทำทุกอย่างคุณสามารถตั้งค่าสถานะการดีบักบน commandline:

$ bash -x ~/bin/ducks
+ du -cks -x dir1 dir2 dir3 file1 file2 file3
+ sort -n
+ tail .ducks
123 etc
424 bin
796 total



$ sh -xv ~/bin/ducks  
#!/usr/bin/env bash

# Find the disk hog
# Borrowed from http://oreilly.com/pub/h/15
...
...

2
เคล็ดลับที่เกี่ยวข้อง: ฉันได้รับนิสัยการวางemulate sh 2>/dev/nullที่ด้านบนของสคริปต์เชลล์ของฉัน เมื่อรันด้วย zsh สิ่งนี้จะทำให้มันเข้าสู่โหมดที่เข้ากันได้กับ POSIX เมื่อทำงานกับกระสุนอื่น ๆ เส้นจะไม่มีผล zsh -x /path/to/scriptจากนั้นฉันก็สามารถเรียกใช้สคริปต์ด้วย ฉันชอบ zsh ที่นี่เพราะมันให้ร่องรอยที่ดีกว่าทุบตีหรือ ksh
Gilles 'หยุดความชั่วร้าย'

7

Shawn J. Goff ทำคะแนนได้ดีมาก แต่ไม่ได้รวมเรื่องราวทั้งหมด:

จริง ๆ แล้ว Ubuntu ไม่ได้ใช้ bash shell แต่เป็นอันที่คล้ายกันมากที่เรียกว่า dash สคริปต์ที่ต้องใช้ทุบตีอาจทำงานผิดพลาดเล็กน้อยเมื่อถูกเรียกโดยทำ/bin/shสคริปต์เพราะคุณเพิ่งเรียกสคริปต์ทุบตีโดยใช้ตัวแปลประ

จำนวนมากของสคริปต์ระบบ (เหมือนใน init.d ใน / etc และอื่น ๆ ) มี shebang #!/bin/shแต่/bin/shในความเป็นจริงการเชื่อมโยงสัญลักษณ์ไปยังเปลือกอื่น - ในครั้งอดีตในปัจจุบัน/bin/bash /bin/dashแต่เมื่อมีการเรียกใช้หนึ่งในนั้นพวกเขาจะ/bin/shทำงานแตกต่างกันเช่นพวกเขายึดติดกับโหมดความเข้ากันได้ POSIX

พวกเขาทำเช่นนี้ได้อย่างไร พวกเขาตรวจสอบว่าพวกเขาถูกเรียกอย่างไร

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

ตามกฎทั่วไป: หากคุณกำลังเรียนรู้เชลล์เฉพาะเช่น bash และเขียนคำสั่งจาก bash tutorial ให้ใส่#!/bin/bashพาดหัวในบรรทัด#!/bin/shยกเว้นที่ระบุไว้เป็นอย่างอื่น มิฉะนั้นคำสั่งของคุณอาจล้มเหลว และถ้าคุณยังไม่ได้เขียนสคริปต์ด้วยตัวเองให้เรียกใช้โดยตรง ( ./foo.sh, bar/foo.sh) แทนที่จะคาดเดาเชลล์ ( sh foo.sh, sh bar/foo.sh) shebang ควรเรียกใช้เปลือกขวา

และนี่เป็นคำขอร้องอีกสองประเภท:

cat foo.sh | dash
dash < foo.sh

5

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

การใช้พา ธ หรือมอบให้กับมันเพื่อ/bin/shสร้างกระบวนการใหม่ที่คำสั่งจะถูกดำเนินการ


2
sh script
bash script

ฉันกำลังคิดว่ามีอีก ...

.และsourceเหมือนกัน หลังจากดำเนินการเปลี่ยนแปลงใด ๆ ของสภาพแวดล้อมในscriptจะถูกเก็บไว้ โดยปกติแล้วจะใช้เป็นแหล่งไลบรารี Bash เพื่อให้ไลบรารีสามารถนำกลับมาใช้ใหม่ได้ในสคริปต์ต่างๆ

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


2
.ใช้งานได้กับ sh / bash และเชลล์ที่เกี่ยวข้องเท่านั้น sourceยังทำงานใน csh และเชลล์ที่เกี่ยวข้อง
KeithB

1

" userland exec " นับเป็นวิธีอื่นหรือไม่? Userland exec โหลดรหัสและเรียกใช้งานโดยไม่ต้องใช้การเรียกระบบ execve ()


1
. ./filename
# ( dot space dot slash filename )

รันสคริปต์ในเชลล์ปัจจุบันเมื่อไดเร็กทอรีไม่ได้อยู่ในพา ธ


1

. และแหล่งที่มาแตกต่างกันเล็กน้อยใน zsh อย่างน้อย (นั่นคือสิ่งที่ฉันใช้) เพราะ

source file

ธิการในขณะที่

. file

ไม่ต้องการ

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