ข้อแตกต่างที่สำคัญเพียงอย่างเดียวคือระหว่างการจัดหาและการเรียกใช้สคริปต์ 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
) แสดงทั้งหมดเพราะตอนนี้จะแสดงแต่ละของพวกเขาเพราะคำสั่งของคุณจะมีคำว่าps
bash
เปิดตัวโดยตรงกับเชลล์ที่แตกต่างกัน ( 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 เมื่อเปิดตัวด้วยตั้งแต่กระบวนการเด็กไม่ได้sh
bash