Sự khác biệt giữa việc thực thi các tập lệnh shell bằng cách sử dụng tập tin nguồn.sh, tập tin, tập tin, tập tin, tập tin. ./file.sh


13

Có một cái nhìn vào mã:

#!/bin/bash
read -p "Eneter 1 for UID and 2 for LOGNAME" choice
if [ $choice -eq 1 ]
then
        read -p "Enter UID:  " uid
        logname=`cat /etc/passwd | grep $uid | cut -f1 -d:`
else
        read -p "Enter Logname:  " logname
fi
not=`ps -au$logname | grep -c bash`
echo  "The number of terminals opened by $logname are $not"

Mã này được sử dụng để tìm ra số lượng thiết bị đầu cuối được mở bởi một người dùng trên cùng một PC. Bây giờ có hai người dùng đăng nhập, nói x và y. Tôi hiện đang đăng nhập là y và có 3 thiết bị đầu cuối mở trong người dùng x. Nếu tôi thực thi mã này trong y bằng các cách khác nhau như đã đề cập ở trên, kết quả là:

$ ./file.sh
The number of terminals opened by x are 3

$ bash file.sh
The number of terminals opened by x are 5

$ sh file.sh
The number of terminals opened by x are 3

$ source file.sh
The number of terminals opened by x are 4

$ . ./file.sh
The number of terminals opened by x are 4

Lưu ý: Tôi đã vượt qua 1 và uid 1000 cho tất cả các tệp thực thi này.

Bây giờ bạn có thể vui lòng giải thích sự khác biệt giữa tất cả những điều này?


sự khác biệt là shell nào được thực thi. sh không bash
j0h

2
Hai lần thực hiện cuối cùng cũng khác nhau vì bạn đang thực hiện trong cùng một bối cảnh. Thêm ở đây
Zaka Elab

Tôi đang cố gắng đếm số lượng bash (ở đây bằng với số thiết bị đầu cuối) được mở bởi người dùng khác (không phải cùng một người dùng nơi chúng tôi đăng nhập) và bạn có thể giải thích tại sao số khác nhau xuất hiện trong mỗi trường hợp
Ramana Reddy

@RamanaReddy người dùng khác có thể đã chạy một tập lệnh hoặc bắt đầu một tab mới. Ai biết?
muru

Câu trả lời:


21

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.shsẽ 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:

  1. ./file.sh

    Điều này sẽ thực thi một tập lệnh được gọi file.shlà 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 $PATHtệ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/commandhoặc ./command, thì $PATHtệp bị bỏ qua và tệp cụ thể đó được thực thi.

  2. ../file.sh

    Điều này về cơ bản giống như ./file.shngoạ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 ( ../).

  3. 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.shtrong thư mục hiện tại. Sự khác biệt là bạn rõ ràng đang chạy nó với shshell. Trên các hệ thống Ubuntu, đó là dashvà 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 scriptsẽ 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ó.

  4. 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 sourcelà 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 foothà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 footrong 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 foocha 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 bashxuấ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ở:

  1. 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.shcó nghĩa là bạn grepsẽ 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 pslệ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 pshiển thị đầu ra của nó.

  2. 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 pssẽ hiển thị bash foo.shvà được tính. Vì vậy, ở đây chúng ta có tiến trình cha, bashtập lệnh đang chạy trình bao nhân bản (đang chạy ps) tất cả được hiển thị bởi vì bây giờ pssẽ hiển thị mỗi trong số chúng vì lệnh của bạn sẽ bao gồm từ bash.

  3. 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 shvà không bash. Do đó, bashví dụ duy nhất là shell cha nơi bạn khởi chạy tập lệnh của mình. shThay vào đó, tất cả các vỏ khác được đề cập ở trên đang được điều hành .

  4. 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 pslệ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 psmà 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 shvì quá trình con không bash.


Khi bạn nói thay thế lệnh khởi chạy một bản sao của shell cha, bản sao này khác với shell phụ như thế nào khi bạn chạy tập lệnh với ./foo.sh?
Didier A.

Và khi bạn chạy pgrep mà không thay thế lệnh, tôi giả sử nó chạy từ trong cùng một shell mà tập lệnh chạy trong? Vì vậy, tương tự như tìm nguồn cung ứng?
Didier A.

@didibus Tôi không chắc ý của bạn là gì. Thay thế lệnh chạy trong một subshell; ./foo.shchạy trong một shell mới không phải là bản sao của cha mẹ. Ví dụ: nếu bạn đặt foo="bar"trong thiết bị đầu cuối của mình và sau đó chạy một tập lệnh thực thi echo $foo, bạn sẽ nhận được một dòng trống vì shell của tập lệnh sẽ không kế thừa giá trị của biến. pgreplà một nhị phân riêng biệt, và vâng, nó được chạy bởi tập lệnh bạn đang chạy.
terdon

Về cơ bản tôi cần làm rõ về: "lưu ý sự vắng mặt của lệnh thay thế". Tại sao chạy nhị phân pgrep từ tập lệnh không thêm shell, nhưng chạy nhị phân ps với lệnh thay thế thì sao? Thứ hai, tôi cần làm rõ về "bản sao của shell cha", nó có giống như một shell phụ trong đó các biến shell của cha mẹ được sao chép sang con không? Tại sao thay thế lệnh làm điều đó?
Didier A.
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.