ข้อแตกต่างที่สำคัญเพียงอย่างเดียวคือระหว่างการจัดหาและการเรียกใช้สคริปต์ source foo.shจะเป็นแหล่งที่มาและตัวอย่างอื่น ๆ ทั้งหมดที่คุณแสดงกำลังดำเนินการ รายละเอียดเพิ่มเติม:
./file.sh
จะดำเนินการสคริปต์ที่เรียกว่าfile.shที่อยู่ในไดเรกทอรีปัจจุบัน ( ./) โดยปกติเมื่อคุณเรียกใช้commandเชลล์จะค้นหาไดเรกทอรีใน$PATHไฟล์เรียกทำงานที่เรียกว่าของcommandคุณ หากคุณให้เส้นทางแบบเต็มเช่น/usr/bin/commandหรือ./commandดังนั้น$PATHจะถูกละเว้นและไฟล์เฉพาะนั้นจะถูกดำเนินการ
../file.sh
โดยทั่วไปจะเหมือนกับ./file.shยกเว้นว่าแทนที่จะค้นหาในไดเรกทอรีปัจจุบันfile.shจะค้นหาในไดเรกทอรีหลัก ( ../)
sh file.sh
สิ่งนี้เทียบเท่ากับsh ./file.shข้างต้นมันจะเรียกใช้สคริปต์ที่เรียกว่าfile.shในไดเรกทอรีปัจจุบัน ความแตกต่างคือคุณกำลังรันมันอย่างชัดเจนด้วยshเชลล์ ในระบบอูบุนตูที่เป็นและไม่ได้dash bashโดยปกติแล้วสคริปต์จะมีบรรทัด shebangที่ให้โปรแกรมที่ควรรัน การเรียกพวกเขาด้วยการแทนที่ที่แตกต่างนั้น ตัวอย่างเช่น:
$ cat foo.sh
#!/bin/bash
## The above is the shebang line, it points to bash
ps h -p $$ -o args='' | cut -f1 -d' ' ## This will print the name of the shell
สคริปต์นั้นจะพิมพ์ชื่อของเชลล์ที่ใช้เพื่อรัน มาดูกันว่ามันจะกลับมาเมื่อใดในวิธีที่แตกต่าง:
$ bash foo.sh
bash
$ sh foo.sh
sh
$ zsh foo.sh
zsh
ดังนั้นการโทรหาสคริปต์ด้วยshell scriptจะเป็นการแทนที่บรรทัด shebang (ถ้ามี) และเรียกใช้สคริปต์ด้วยเชลล์อะไรก็ตามที่คุณบอก
source file.sh หรือ . file.sh
สิ่งนี้เรียกว่าน่าแปลกใจพอที่จะจัดหาสคริปต์ คำสำคัญsourceคือนามแฝงของ.คำสั่งshell builtin นี่เป็นวิธีการเรียกใช้งานสคริปต์ภายในเชลล์ปัจจุบัน โดยปกติเมื่อเรียกใช้งานสคริปต์สคริปต์จะรันในเชลล์ของตัวเองซึ่งแตกต่างจากสคริปต์ปัจจุบัน เพื่อแสดง:
$ cat foo.sh
#!/bin/bash
foo="Script"
echo "Foo (script) is $foo"
ตอนนี้ถ้าฉันตั้งค่าตัวแปรfooเป็นอย่างอื่นในเปลือกแม่แล้วเรียกใช้สคริปต์สคริปต์จะพิมพ์ค่าที่แตกต่างกันของfoo(เพราะมันตั้งอยู่ภายในสคริปต์) แต่ค่าของfooในเปลือกแม่จะไม่เปลี่ยนแปลง:
$ foo="Parent"
$ bash foo.sh
Foo (script) is Script ## This is the value from the script's shell
$ echo "$foo"
Parent ## The value in the parent shell is unchanged
อย่างไรก็ตามถ้าฉันแหล่งสคริปต์แทนการดำเนินการมันจะถูกเรียกใช้ในเชลล์เดียวกันดังนั้นค่าของfooในแม่จะมีการเปลี่ยนแปลง:
$ source ./foo.sh
Foo (script) is Script ## The script's foo
$ echo "$foo"
Script ## Because the script was sourced,
## the value in the parent shell has changed
ดังนั้นการจัดหาจะใช้ในบางกรณีที่คุณต้องการให้สคริปต์ส่งผลกระทบต่อเชลล์ที่คุณใช้งานอยู่ โดยทั่วไปจะใช้เพื่อกำหนดตัวแปรเชลล์และให้พร้อมใช้งานหลังจากสคริปต์เสร็จสิ้น
เหตุผลที่คุณได้รับคำตอบที่แตกต่างคือประการแรกสคริปต์ของคุณไม่ได้ทำในสิ่งที่คุณคิด มันนับจำนวนครั้งที่ปรากฏในการส่งออกของbash นี่ไม่ใช่จำนวนเทอร์มินัลเปิดเป็นจำนวนเชลล์ที่ใช้งาน (ที่จริงแล้วมันไม่ได้เป็นเช่นนั้น แต่นั่นเป็นอีกการสนทนา) เพื่อให้ชัดเจนขึ้นฉันทำให้สคริปต์ของคุณง่ายขึ้นเล็กน้อย:ps
#!/bin/bash
logname=terdon
not=`ps -au$logname | grep -c bash`
echo "The number of shells opened by $logname is $not"
และเรียกใช้ด้วยวิธีการต่าง ๆ โดยเปิดเพียงเทอร์มินัลเดียว:
การเปิดตัวโดยตรง, ./foo.sh.
$ ./foo.sh
The number of shells opened by terdon is 1
ที่นี่คุณใช้สาย shebang ซึ่งหมายความว่าสคริปต์จะถูกเรียกใช้งานโดยตรงจากสิ่งที่ตั้งไว้ที่นั่น psนี้มีผลต่อวิธีการที่สคริปต์ที่ปรากฏอยู่ในการส่งออกของ แทนที่จะแสดงเป็นbash foo.shมันจะปรากฏเฉพาะfoo.shซึ่งหมายความว่าคุณgrepจะพลาด การปกครอง, ทุบตีใช้สคริปต์: มีจริง 3 กรณีทุบตีทำงานเป็นและอีกคนหนึ่งซึ่งทำงานpsคำสั่ง สิ่งสุดท้ายนี้มีความสำคัญการเรียกใช้คำสั่งที่มีการทดแทนคำสั่ง ( `command`หรือ$(command)) ส่งผลให้เกิดสำเนาของเชลล์พาเรนต์ที่จะเปิดตัว อย่างไรก็ตามที่นี่ไม่มีการแสดงสิ่งเหล่านี้เนื่องจากวิธีการpsแสดงผลลัพธ์
เรียกใช้โดยตรงด้วยเชลล์ (bash) ที่ชัดเจน
$ bash foo.sh
The number of shells opened by terdon is 3
ที่นี่เนื่องจากคุณกำลังใช้งานbash foo.shเอาต์พุตของpsจะแสดงbash foo.shและถูกนับ ดังนั้นที่นี่เรามีการปกครองที่bashใช้สคริปต์และเปลือกโคลน (เรียกใช้ps) แสดงทั้งหมดเพราะตอนนี้จะแสดงแต่ละของพวกเขาเพราะคำสั่งของคุณจะมีคำว่าpsbash
เปิดตัวโดยตรงกับเชลล์ที่แตกต่างกัน ( sh)
$ sh foo.sh
The number of shells opened by terdon is 1
ซึ่งจะแตกต่างเพราะคุณกำลังเรียกใช้สคริปต์ด้วยและไม่sh bashดังนั้นbashอินสแตนซ์เดียวคือเชลล์พาเรนต์ที่คุณเปิดใช้งานสคริปต์ เชลล์อื่น ๆ ทั้งหมดที่กล่าวถึงข้างต้นถูกเรียกใช้โดยshแทน
การจัดหา (ทั้งโดย.หรือsourceในสิ่งเดียวกัน)
$ . ./foo.sh
The number of shells opened by terdon is 2
ดังที่ฉันได้อธิบายไว้ข้างต้นการจัดหาสคริปต์ทำให้สคริปต์ทำงานในเชลล์เดียวกับกระบวนการหลัก อย่างไรก็ตามเชลล์ย่อยที่แยกต่างหากเริ่มที่จะเรียกใช้งานpsคำสั่งและนำมาซึ่งผลรวมถึงสอง
ในฐานะที่เป็นบันทึกสุดท้ายวิธีที่ถูกต้องในการนับกระบวนการทำงานไม่ได้ที่จะแยกแต่กับการใช้งานps pgrepคุณจะหลีกเลี่ยงปัญหาเหล่านี้ทั้งหมดได้หรือไม่
pgrep -cu terdon bash
ดังนั้นเวอร์ชันการทำงานของสคริปต์ของคุณที่พิมพ์หมายเลขที่ถูกต้องเสมอคือ (สังเกตว่าไม่มีการทดแทนคำสั่ง):
#!/usr/bin/env bash
user="terdon"
printf "Open shells:"
pgrep -cu "$user" bash
สิ่งนี้จะส่งคืน 1 เมื่อมีที่มาและ 2 (เนื่องจากมีการเปิดใช้งาน bash ใหม่เพื่อเรียกใช้สคริปต์) สำหรับวิธีการเปิดตัวอื่น ๆ ทั้งหมด มันจะยังคงกลับ 1 เมื่อเปิดตัวด้วยตั้งแต่กระบวนการเด็กไม่ได้shbash