Sự khác biệt lớn duy nhất là giữa tìm nguồn cung ứng và thực thi một kịch bản. source foo.sh
sẽ lấy nó và tất cả các ví dụ khác mà bạn hiển thị đang thực thi. Chi tiết hơn:
./file.sh
Điều này sẽ thực thi một tập lệnh được gọi file.sh
là trong thư mục hiện tại ( ./
). Thông thường, khi bạn chạy command
, shell sẽ xem qua các thư mục trong $PATH
tệp của bạn để tìm tệp thực thi được gọi command
. Nếu bạn đưa ra một đường dẫn đầy đủ, chẳng hạn như /usr/bin/command
hoặc ./command
, thì $PATH
tệp bị bỏ qua và tệp cụ thể đó được thực thi.
../file.sh
Điều này về cơ bản giống như ./file.sh
ngoại trừ thay vì tìm trong thư mục hiện tại file.sh
, nó đang tìm trong thư mục cha ( ../
).
sh file.sh
Điều này tương đương với sh ./file.sh
, như trên nó sẽ chạy tập lệnh được gọi file.sh
trong thư mục hiện tại. Sự khác biệt là bạn rõ ràng đang chạy nó với sh
shell. Trên các hệ thống Ubuntu, đó là dash
và không bash
. Thông thường, các tập lệnh có một dòng shebang cung cấp cho chương trình mà chúng nên được chạy như. Gọi họ bằng một cái khác ghi đè lên điều đó. Ví dụ:
$ 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
Kịch bản lệnh đó sẽ chỉ in tên của shell được sử dụng để chạy nó. Chúng ta hãy xem những gì nó trả về khi được gọi theo những cách khác nhau:
$ bash foo.sh
bash
$ sh foo.sh
sh
$ zsh foo.sh
zsh
Vì vậy, việc gọi việc gọi một tập lệnh shell script
sẽ ghi đè lên dòng shebang (nếu có) và chạy tập lệnh với bất kỳ shell nào bạn nói với nó.
source file.sh
hoặc là . file.sh
Điều này được gọi, đủ đáng ngạc nhiên, tìm nguồn cung ứng kịch bản. Từ khóa source
là một bí danh cho .
lệnh dựng sẵn shell . Đây là một cách để thực thi tập lệnh trong trình bao hiện tại. Thông thường, khi một tập lệnh được thực thi, nó được chạy trong shell riêng của nó khác với tập lệnh hiện tại. Để minh họa:
$ cat foo.sh
#!/bin/bash
foo="Script"
echo "Foo (script) is $foo"
Bây giờ, nếu tôi đặt biến foo
thành một thứ khác trong vỏ cha và sau đó chạy tập lệnh, tập lệnh sẽ in một giá trị khác foo
(vì nó cũng được đặt trong tập lệnh) nhưng giá trị của foo
trong vỏ cha sẽ không thay đổi:
$ 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
Tuy nhiên, nếu tôi lấy tập lệnh thay vì thực thi nó, nó sẽ được chạy trong cùng một shell nên giá trị của foo
cha sẽ bị thay đổi:
$ 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
Vì vậy, tìm nguồn cung ứng được sử dụng trong một số trường hợp bạn muốn tập lệnh ảnh hưởng đến trình bao bạn đang chạy nó. Nó thường được sử dụng để xác định các biến shell và có sẵn chúng sau khi tập lệnh kết thúc.
Với tất cả những gì trong tâm trí, lý do bạn nhận được câu trả lời khác nhau, trước hết, là kịch bản của bạn không làm những gì bạn nghĩ. Nó đếm số lần bash
xuất hiện trong đầu ra của ps
. Đây không phải là số lượng thiết bị đầu cuối mở , nó là số lượng vỏ đang chạy (trên thực tế, nó thậm chí không phải vậy, nhưng đó là một cuộc thảo luận khác). Để làm rõ, tôi đã đơn giản hóa kịch bản của bạn một chút để làm điều này:
#!/bin/bash
logname=terdon
not=`ps -au$logname | grep -c bash`
echo "The number of shells opened by $logname is $not"
Và chạy nó theo nhiều cách khác nhau chỉ với một thiết bị đầu cuối mở:
Ra mắt trực tiếp ./foo.sh
,.
$ ./foo.sh
The number of shells opened by terdon is 1
Ở đây, bạn đang sử dụng dòng shebang. Điều này có nghĩa là tập lệnh được thực thi trực tiếp bởi bất cứ điều gì được đặt ở đó. Điều này ảnh hưởng đến cách tập lệnh được hiển thị trong đầu ra của ps
. Thay vì được liệt kê như bash foo.sh
, nó sẽ chỉ được hiển thị như foo.sh
có nghĩa là bạn grep
sẽ bỏ lỡ nó. Thực tế có 3 trường hợp bash đang chạy: tiến trình cha, bash chạy tập lệnh và một trường hợp khác chạy ps
lệnh . Điều cuối cùng này rất quan trọng, khởi chạy một lệnh với sự thay thế lệnh ( `command`
hoặc $(command)
) dẫn đến một bản sao của shell cha được khởi chạy và chạy lệnh đó. Tuy nhiên, ở đây, không ai trong số này được hiển thị vì cách ps
hiển thị đầu ra của nó.
Khởi chạy trực tiếp với vỏ (bash) rõ ràng
$ bash foo.sh
The number of shells opened by terdon is 3
Ở đây, vì bạn đang chạy với bash foo.sh
, đầu ra của ps
sẽ hiển thị bash foo.sh
và được tính. Vì vậy, ở đây chúng ta có tiến trình cha, bash
tập lệnh đang chạy và trình bao nhân bản (đang chạy ps
) tất cả được hiển thị bởi vì bây giờ ps
sẽ hiển thị mỗi trong số chúng vì lệnh của bạn sẽ bao gồm từ bash
.
Khởi chạy trực tiếp với shell khác ( sh
)
$ sh foo.sh
The number of shells opened by terdon is 1
Điều này là khác nhau bởi vì bạn đang chạy tập lệnh với sh
và không bash
. Do đó, bash
ví dụ duy nhất là shell cha nơi bạn khởi chạy tập lệnh của mình. sh
Thay vào đó, tất cả các vỏ khác được đề cập ở trên đang được điều hành .
Tìm nguồn cung ứng (bằng .
hoặc source
, cùng một thứ)
$ . ./foo.sh
The number of shells opened by terdon is 2
Như tôi đã giải thích ở trên, việc tìm nguồn cung cấp một tập lệnh khiến nó chạy trong cùng trình bao với tiến trình cha. Tuy nhiên, một lớp con riêng biệt được bắt đầu để khởi chạy ps
lệnh và điều đó mang lại tổng số cho hai.
Như một lưu ý cuối cùng, cách chính xác để đếm các tiến trình đang chạy không phải là phân tích cú pháp ps
mà là sử dụng pgrep
. Tất cả những vấn đề này sẽ tránh được nếu bạn chạy
pgrep -cu terdon bash
Vì vậy, một phiên bản hoạt động của tập lệnh của bạn luôn in đúng số là (lưu ý không có sự thay thế lệnh):
#!/usr/bin/env bash
user="terdon"
printf "Open shells:"
pgrep -cu "$user" bash
Điều đó sẽ trả về 1 khi có nguồn gốc và 2 (vì một bash mới sẽ được khởi chạy để chạy tập lệnh) cho tất cả các cách khởi chạy khác. Nó vẫn sẽ trả về 1 khi khởi chạy cùng với sh
vì quá trình con không bash
.