Làm thế nào để phát hiện nếu một tập lệnh đang có nguồn gốc


217

Tôi có một kịch bản mà tôi không muốn nó gọi exitnếu nó có nguồn gốc.

Tôi đã nghĩ đến việc kiểm tra xem $0 == bashnhưng điều này có vấn đề nếu tập lệnh được lấy từ tập lệnh khác hay nếu người dùng lấy nó từ một trình bao khác ksh.

Có cách nào đáng tin cậy để phát hiện nếu một tập lệnh đang có nguồn gốc không?


2
Tôi đã có một vấn đề tương tự một lúc trước và giải quyết nó bằng cách tránh 'thoát' trong mọi trường hợp; "Kill -INT $$" chấm dứt tập lệnh một cách an toàn trong cả hai trường hợp.
JESii

1
Bạn có nhận thấy câu trả lời này ? Nó được đưa ra 5 năm sau kể từ khi được chấp nhận, nhưng nó có "pin đi kèm".
raratiru

Câu trả lời:


72

Điều này dường như có thể di động giữa Bash và Korn:

[[ $_ != $0 ]] && echo "Script is being sourced" || echo "Script is a subshell"

Một dòng tương tự như này hoặc một nhiệm vụ như pathname="$_"(với một thử nghiệm và hành động sau này) phải nằm trên dòng đầu tiên của tập lệnh hoặc trên dòng sau shebang (nếu được sử dụng, nên dành cho ksh để nó hoạt động theo hoàn cảnh nhất).


10
Thật không may, nó không được đảm bảo để làm việc. Nếu người dùng đã thiết lập BASH_ENV, $_ở đầu tập lệnh sẽ là lệnh cuối cùng chạy từ đó BASH_ENV.
Mikel

30
Điều này cũng sẽ không hoạt động nếu bạn sử dụng bash để thực thi tập lệnh, ví dụ $ bash script.sh thì $ _ sẽ là / bin / bash thay vì ./script.sh, đó là trường hợp bạn mong đợi, khi tập lệnh được gọi theo cách này: $ ./script.sh Trong mọi trường hợp phát hiện có $_vấn đề.
Wi Girls Purwanto

2
Các xét nghiệm bổ sung có thể được đưa vào để kiểm tra các phương thức gọi đó.
Tạm dừng cho đến khi có thông báo mới.

8
Thật không may, đó là sai! xem câu trả lời của tôi
F. Hauri

8
Tóm lại: Mặc dù cách tiếp cận này thường hoạt động, nhưng nó không mạnh mẽ ; nó đã thất bại trong 2 trường hợp sau: (a) bash script(gọi qua shell thực thi, mà giải pháp này khai thác sai là có nguồn gốc ) và (b) (rất ít khả năng) echo bash; . script(nếu $_xảy ra để khớp với shell tìm kịch bản, giải pháp này đánh lừa nó là một subshell ). Chỉ các biến đặc biệt dành riêng cho vỏ (ví dụ $BASH_SOURCE:) cho phép các giải pháp mạnh mẽ (theo sau là không có giải pháp tương thích POSIX mạnh mẽ). Nó có thể, mặc dù cồng kềnh, đưa ra một bài kiểm tra chéo vỏ mạnh mẽ.
mkuity0

170

Nếu phiên bản Bash của bạn biết về biến mảng BASH_SOURCE, hãy thử một số thứ như:

# man bash | less -p BASH_SOURCE
#[[ ${BASH_VERSINFO[0]} -le 2 ]] && echo 'No BASH_SOURCE array variable' && exit 1

[[ "${BASH_SOURCE[0]}" != "${0}" ]] && echo "script ${BASH_SOURCE[0]} is being sourced ..."

11
Đó có thể là cách sạch nhất vì $ BASH_SOURCE được dành cho mục đích đó chính xác.
con-f-sử dụng

4
Lưu ý rằng điều này sẽ không hoạt động theo ksh, đó là một điều kiện mà OP đã chỉ định.
Tạm dừng cho đến khi có thông báo mới.

2
Có một lý do để sử dụng ${BASH_SOURCE[0]}thay vì chỉ $BASH_SOURCE? Và ${0}vs $0?
hraban

4
BASH_SOURCElà một biến mảng (xem hướng dẫn sử dụng ) chứa một dấu vết ngăn xếp của các nguồn, trong đó ${BASH_SOURCE[0]}là biến mới nhất. Các dấu ngoặc nhọn được sử dụng ở đây để cho bash biết đâu là một phần của tên biến. Chúng không cần thiết $0trong trường hợp này, nhưng chúng cũng không gây hại. ;)
Konrad

4
@Konrad, và nếu bạn mở rộng $array, bạn sẽ nhận được ${array[0]}theo mặc định. Vì vậy, một lần nữa, có một lý do [...]?
Charles Duffy

133

Các giải pháp mạnh mẽ cho bash, ksh,zsh , bao gồm một cross-vỏ một, cộng với một giải pháp POSIX-compliant mạnh mẽ một cách hợp lý :

  • Các số phiên bản được cung cấp là những số mà chức năng đã được xác minh - rất có thể, các giải pháp này cũng hoạt động trên các phiên bản cũ hơn nhiều - phản hồi rất đáng hoan nghênh .

  • Chỉ sử dụng các tính năng POSIX (chẳng hạn như trong dash, hoạt động như /bin/shtrên Ubuntu), không có cách nào mạnh mẽ để xác định xem tập lệnh có nguồn gốc hay không - xem bên dưới để biết gần đúng nhất .

Một lớp lót theo sau - giải thích dưới đây; phiên bản vỏ chéo rất phức tạp, nhưng nó sẽ hoạt động mạnh mẽ:

  • bash (được xác minh vào ngày 3.57 và 4.4.19)

    (return 0 2>/dev/null) && sourced=1 || sourced=0
  • ksh (đã xác minh trên 93u +)

    [[ $(cd "$(dirname -- "$0")" && 
       printf '%s' "${PWD%/}/")$(basename -- "$0") != "${.sh.file}" ]] &&
         sourced=1 || sourced=0
    
  • zsh (được xác minh vào ngày 5.0.5) - hãy chắc chắn gọi hàm này bên ngoài hàm

    [[ $ZSH_EVAL_CONTEXT =~ :file$ ]] && sourced=1 || sourced=0
  • vỏ chéo (bash, ksh, zsh)

    ([[ -n $ZSH_EVAL_CONTEXT && $ZSH_EVAL_CONTEXT =~ :file$ ]] || 
     [[ -n $KSH_VERSION && $(cd "$(dirname -- "$0")" &&
        printf '%s' "${PWD%/}/")$(basename -- "$0") != "${.sh.file}" ]] || 
     [[ -n $BASH_VERSION ]] && (return 0 2>/dev/null)) && sourced=1 || sourced=0
    
  • Tuân thủ POSIX ; không phải là một lớp lót (đường ống đơn) vì lý do kỹ thuật và không hoàn toàn mạnh mẽ (xem dưới cùng):

    sourced=0
    if [ -n "$ZSH_EVAL_CONTEXT" ]; then 
      case $ZSH_EVAL_CONTEXT in *:file) sourced=1;; esac
    elif [ -n "$KSH_VERSION" ]; then
      [ "$(cd $(dirname -- $0) && pwd -P)/$(basename -- $0)" != "$(cd $(dirname -- ${.sh.file}) && pwd -P)/$(basename -- ${.sh.file})" ] && sourced=1
    elif [ -n "$BASH_VERSION" ]; then
      (return 0 2>/dev/null) && sourced=1 
    else # All other shells: examine $0 for known shell binary filenames
      # Detects `sh` and `dash`; add additional shell filenames as needed.
      case ${0##*/} in sh|dash) sourced=1;; esac
    fi
    

Giải trình:


bash

(return 0 2>/dev/null) && sourced=1 || sourced=0

Lưu ý: Kỹ thuật này được điều chỉnh từ câu trả lời của người dùng575163 , vì hóa ra nó mạnh hơn giải pháp ban đầu, [[ $0 != "$BASH_SOURCE" ]] && sourced=1 || sourced=0[1]

  • Bash chỉ cho phép các returncâu lệnh từ các hàm và, trong phạm vi cấp cao nhất của tập lệnh, chỉ khi tập lệnh có nguồn gốc .

    • Nếu returnđược sử dụng trong phạm vi cấp cao nhất của tập lệnh không có nguồn gốc , thông báo lỗi sẽ được phát ra và mã thoát được đặt thành 1.
  • (return 0 2>/dev/null)thực thi returntrong một subshell và loại bỏ thông báo lỗi; sau đó mã thoát cho biết tập lệnh có nguồn gốc ( 0) hay không ( 1), được sử dụng với các toán tử &&||để đặt sourcedbiến tương ứng.

    • Việc sử dụng một lớp con là cần thiết, bởi vì thực thi returntrong phạm vi cấp cao nhất của tập lệnh có nguồn gốc sẽ thoát khỏi tập lệnh.
    • Mẹo đội mũ cho @Haozhun , người đã khiến lệnh trở nên mạnh mẽ hơn bằng cách sử dụng rõ ràng 0làm returntoán hạng; ông lưu ý: mỗi bash trợ giúp của return [N]: "Nếu N bị bỏ qua, trạng thái trả về là trạng thái của lệnh cuối cùng." Do đó, phiên bản trước đó [chỉ sử dụng return, không có toán hạng] tạo ra kết quả không chính xác nếu lệnh cuối cùng trên trình bao của người dùng có giá trị trả về khác không.

ksh

[[ \
   $(cd "$(dirname -- "$0")" && printf '%s' "${PWD%/}/")$(basename -- "$0") != \
   "${.sh.file}" \
]] && 
sourced=1 || sourced=0

Biến đặc biệt ${.sh.file}có phần giống với $BASH_SOURCE; lưu ý rằng ${.sh.file}gây ra lỗi cú pháp trong bash, zsh và dash, vì vậy hãy chắc chắn thực hiện nó một cách có điều kiện trong các tập lệnh đa shell.

Không giống như trong bash, $0${.sh.file}KHÔNG đảm bảo được chính xác giống hệt nhau trong trường hợp không có nguồn gốc, như $0thể có một tương đối con đường, trong khi ${.sh.file}luôn luôn là một đầy đủ đường, vì vậy $0phải được giải quyết với một đường dẫn đầy đủ trước khi so sánh.


zsh

[[ $ZSH_EVAL_CONTEXT =~ :file$ ]] && sourced=1 || sourced=0

$ZSH_EVAL_CONTEXTchứa thông tin về bối cảnh đánh giá - gọi hàm này bên ngoài hàm. Bên trong một tập lệnh có nguồn gốc [phạm vi cấp cao nhất], $ZSH_EVAL_CONTEXT kết thúc bằng :file.

Nên biết trước: Bên trong một lệnh thay thế, gắn thêm zsh :cmdsubst, vì vậy kiểm tra $ZSH_EVAL_CONTEXTcho :file:cmdsubst$có.


Chỉ sử dụng các tính năng POSIX

Nếu bạn sẵn sàng đưa ra một số giả định nhất định, bạn có thể đưa ra dự đoán hợp lý, nhưng không lừa đảo về việc liệu tập lệnh của bạn có được cung cấp hay không, dựa trên việc biết tên tệp nhị phân của trình bao có thể thực thi tập lệnh của bạn .
Đáng chú ý, điều này có nghĩa là cách tiếp cận này thất bại nếu tập lệnh của bạn đang được lấy bởi tập lệnh khác .

Phần "Cách xử lý các yêu cầu có nguồn gốc" trong câu trả lời này của tôi thảo luận về các trường hợp cạnh không thể xử lý bằng các tính năng POSIX chỉ chi tiết.

Điều này phụ thuộc vào hành vi tiêu chuẩn của $0, zshví dụ, không thể hiện.

Do đó, cách tiếp cận an toàn nhất là kết hợp các phương pháp mạnh mẽ, dành riêng cho vỏ ở trên với giải pháp dự phòng cho tất cả các vỏ còn lại.

Mẹo đội mũ cho Stéphane Desneuxcâu trả lời của anh ấy cho cảm hứng (chuyển đổi biểu thức tuyên bố vỏ chéo của tôi thành một tuyên bố shtương thích ifvà thêm một trình xử lý cho các shell khác).

sourced=0
if [ -n "$ZSH_EVAL_CONTEXT" ]; then 
  case $ZSH_EVAL_CONTEXT in *:file) sourced=1;; esac
elif [ -n "$KSH_VERSION" ]; then
  [ "$(cd $(dirname -- $0) && pwd -P)/$(basename -- $0)" != "$(cd $(dirname -- ${.sh.file}) && pwd -P)/$(basename -- ${.sh.file})" ] && sourced=1
elif [ -n "$BASH_VERSION" ]; then
  (return 0 2>/dev/null) && sourced=1 
else # All other shells: examine $0 for known shell binary filenames
  # Detects `sh` and `dash`; add additional shell filenames as needed.
  case ${0##*/} in sh|dash) sourced=1;; esac
fi

[1] user1902689 đã phát hiện ra [[ $0 != "$BASH_SOURCE" ]]kết quả dương tính giả khi bạn thực thi tập lệnh nằm trong phần$PATH bằng cách chuyển tên tệp đơn thuần của nó sang tệpbash nhị phân; ví dụ, bash my-scriptbởi vì $0sau đó chỉ là my-script, trong khi đó $BASH_SOURCEđường dẫn đầy đủ . Trong khi bạn thường sẽ không sử dụng kỹ thuật này để gọi script trong $PATH- bạn muốn chỉ cần gọi họ trực tiếp ( my-script) - nó hữu ích khi kết hợp với -xcho gỡ lỗi .


1
Kudos cho một câu trả lời toàn diện như vậy.
DrUseful

75

Sau khi đọc câu trả lời của @ DennisWilliamson, có một số vấn đề, xem bên dưới:

Khi câu hỏi này đại diện cho , có một phần khác trong câu trả lời này liên quan đến ... xem bên dưới.

Đơn giản đường

[ "$0" = "$BASH_SOURCE" ]

Hãy thử (nhanh chóng vì bash đó có thể ;-):

source <(echo $'#!/bin/bash
           [ "$0" = "$BASH_SOURCE" ] && v=own || v=sourced;
           echo "process $$ is $v ($0, $BASH_SOURCE)" ')
process 29301 is sourced (bash, /dev/fd/63)

bash <(echo $'#!/bin/bash
           [ "$0" = "$BASH_SOURCE" ] && v=own || v=sourced;
           echo "process $$ is $v ($0, $BASH_SOURCE)" ')
process 16229 is own (/dev/fd/63, /dev/fd/63)

Tôi sử dụng sourcethay vì tắt .để dễ đọc (như .là một bí danh source):

. <(echo $'#!/bin/bash
           [ "$0" = "$BASH_SOURCE" ] && v=own || v=sourced;
           echo "process $$ is $v ($0, $BASH_SOURCE)" ')
process 29301 is sourced (bash, /dev/fd/63)

Lưu ý rằng số quy trình không thay đổi trong khi quy trình vẫn có nguồn gốc :

echo $$
29301

Tại sao không sử dụng $_ == $0so sánh

Để đảm bảo nhiều trường hợp, tôi bắt đầu viết một kịch bản thực sự :

#!/bin/bash

# As $_ could be used only once, uncomment one of two following lines

#printf '_="%s", 0="%s" and BASH_SOURCE="%s"\n' "$_" "$0" "$BASH_SOURCE"
[[ "$_" != "$0" ]] && DW_PURPOSE=sourced || DW_PURPOSE=subshell

[ "$0" = "$BASH_SOURCE" ] && BASH_KIND_ENV=own || BASH_KIND_ENV=sourced;
echo "proc: $$[ppid:$PPID] is $BASH_KIND_ENV (DW purpose: $DW_PURPOSE)"

Sao chép này vào một tệp có tên testscript:

cat >testscript   
chmod +x testscript

Bây giờ chúng tôi có thể kiểm tra:

./testscript 
proc: 25758[ppid:24890] is own (DW purpose: subshell)

Vậy là được rồi.

. ./testscript 
proc: 24890[ppid:24885] is sourced (DW purpose: sourced)

source ./testscript 
proc: 24890[ppid:24885] is sourced (DW purpose: sourced)

Vậy là được rồi.

Nhưng, để kiểm tra tập lệnh trước khi thêm -xcờ:

bash ./testscript 
proc: 25776[ppid:24890] is own (DW purpose: sourced)

Hoặc để sử dụng các biến được xác định trước:

env PATH=/tmp/bintemp:$PATH ./testscript 
proc: 25948[ppid:24890] is own (DW purpose: sourced)

env SOMETHING=PREDEFINED ./testscript 
proc: 25972[ppid:24890] is own (DW purpose: sourced)

Điều này sẽ không làm việc nữa.

Chuyển nhận xét từ dòng thứ 5 sang thứ 6 sẽ cho câu trả lời dễ đọc hơn:

./testscript 
_="./testscript", 0="./testscript" and BASH_SOURCE="./testscript"
proc: 26256[ppid:24890] is own

. testscript 
_="_filedir", 0="bash" and BASH_SOURCE="testscript"
proc: 24890[ppid:24885] is sourced

source testscript 
_="_filedir", 0="bash" and BASH_SOURCE="testscript"
proc: 24890[ppid:24885] is sourced

bash testscript 
_="/bin/bash", 0="testscript" and BASH_SOURCE="testscript"
proc: 26317[ppid:24890] is own

env FILE=/dev/null ./testscript 
_="/usr/bin/env", 0="./testscript" and BASH_SOURCE="./testscript"
proc: 26336[ppid:24890] is own

Khó hơn: hiện nay...

Vì tôi không dùng Rất nhiều, sau khi một số đọc trên trang người đàn ông, có cố gắng của tôi:

#!/bin/ksh

set >/tmp/ksh-$$.log

Sao chép này trong một testfile.ksh:

cat >testfile.ksh
chmod +x testfile.ksh

Hơn hai lần chạy nó:

./testfile.ksh
. ./testfile.ksh

ls -l /tmp/ksh-*.log
-rw-r--r-- 1 user user   2183 avr 11 13:48 /tmp/ksh-9725.log
-rw-r--r-- 1 user user   2140 avr 11 13:48 /tmp/ksh-9781.log

echo $$
9725

và nhìn thấy:

diff /tmp/ksh-{9725,9781}.log | grep ^\> # OWN SUBSHELL:
> HISTCMD=0
> PPID=9725
> RANDOM=1626
> SECONDS=0.001
>   lineno=0
> SHLVL=3

diff /tmp/ksh-{9725,9781}.log | grep ^\< # SOURCED:
< COLUMNS=152
< HISTCMD=117
< LINES=47
< PPID=9163
< PS1='$ '
< RANDOM=29667
< SECONDS=23.652
<   level=1
<   lineno=1
< SHLVL=2

Có một số biến được di truyền trong một hoạt động có nguồn gốc , nhưng không có gì thực sự liên quan ...

Bạn thậm chí có thể kiểm tra $SECONDSgần 0.000, nhưng điều đó chỉ đảm bảo các trường hợp có nguồn gốc thủ công ...

Bạn thậm chí có thể thử để kiểm tra những gì cha mẹ là:

Đặt cái này vào testfile.ksh:

ps $PPID

Hơn:

./testfile.ksh
  PID TTY      STAT   TIME COMMAND
32320 pts/4    Ss     0:00 -ksh

. ./testfile.ksh
  PID TTY      STAT   TIME COMMAND
32319 ?        S      0:00 sshd: user@pts/4

hoặc ps ho cmd $PPID, nhưng công việc này chỉ dành cho một cấp độ ...

Xin lỗi, tôi không thể tìm thấy một cách đáng tin cậy để làm điều đó, dưới .


[ "$0" = "$BASH_SOURCE" ] || [ -z "$BASH_SOURCE" ]cho các tập lệnh đọc qua ống ( cat script | bash).
hakre

2
Lưu ý rằng đó .không phải là bí danh source, thực ra nó là cách khác. source somescript.shlà một Bash-ism và không phải là di động, . somescript.shlà POSIX và IIRC di động.
dragon788

32

Các BASH_SOURCE[]câu trả lời (bash-3.0 và sau này) có vẻ đơn giản, mặc dù BASH_SOURCE[]được không ghi nhận để làm việc bên ngoài một cơ quan chức năng (nó hiện xảy ra để làm việc, bất đồng với man page).

Cách mạnh mẽ nhất, như được đề xuất bởi WiINA Purwanto, là kiểm tra FUNCNAME[1] trong một chức năng :

function mycheck() { declare -p FUNCNAME; }
mycheck

Sau đó:

$ bash sourcetest.sh
declare -a FUNCNAME='([0]="mycheck" [1]="main")'
$ . sourcetest.sh
declare -a FUNCNAME='([0]="mycheck" [1]="source")'

Điều này tương đương với việc kiểm tra đầu ra của caller, các giá trị mainsourcephân biệt bối cảnh của người gọi. Sử dụng FUNCNAME[]giúp bạn nắm bắt và phân tích callerđầu ra. Bạn cần phải biết hoặc tính toán độ sâu cuộc gọi địa phương của bạn để được chính xác mặc dù. Các trường hợp như một tập lệnh được lấy nguồn từ bên trong một chức năng hoặc tập lệnh khác sẽ làm cho mảng (ngăn xếp) sâu hơn. ( FUNCNAMElà một biến mảng bash đặc biệt, nó nên có các chỉ mục liền kề tương ứng với ngăn xếp cuộc gọi, miễn là không bao giờ unset.)

function issourced() {
    [[ ${FUNCNAME[@]: -1} == "source" ]]
}

(Trong bash-4.2 trở lên, bạn có thể sử dụng biểu mẫu đơn giản hơn ${FUNCNAME[-1]}thay cho mục cuối cùng trong mảng. Được cải thiện và đơn giản hóa nhờ nhận xét của Dennis Williamson bên dưới.)

Tuy nhiên, vấn đề của bạn như đã nêu là " Tôi có một kịch bản mà tôi không muốn nó gọi là 'thoát' nếu nó có nguồn gốc ". Thành bashngữ phổ biến cho tình huống này là:

return 2>/dev/null || exit

Nếu tập lệnh đang được lấy nguồn thì returnsẽ chấm dứt tập lệnh có nguồn gốc và trả về cho người gọi.

Nếu tập lệnh đang được thực thi thì returnsẽ trả về lỗi (được chuyển hướng) và exitsẽ chấm dứt tập lệnh như bình thường. Cả hai returnexitcó thể lấy một mã thoát, nếu cần.

Đáng buồn thay, điều này không hoạt động ksh(ít nhất là không phải trong phiên bản dẫn xuất AT & T mà tôi có ở đây), nó xử lý returntương đương với exitnếu được gọi bên ngoài một hàm hoặc tập lệnh có dấu chấm.

Đã cập nhật : Những gì bạn có thể làm trong các phiên bản hiện đại kshlà kiểm tra biến đặc biệt .sh.levelđược đặt thành độ sâu của hàm gọi. Đối với một tập lệnh được gọi, ban đầu nó sẽ không được đặt, đối với tập lệnh có nguồn gốc, nó sẽ được đặt thành 1.

function issourced {
    [[ ${.sh.level} -eq 2 ]]
}

issourced && echo this script is sourced

Điều này không hoàn toàn mạnh mẽ như phiên bản bash, bạn phải gọi issourced()tệp bạn đang kiểm tra ở cấp cao nhất hoặc ở độ sâu chức năng đã biết.

(Bạn cũng có thể quan tâm đến mã này trên github, sử dụng kshchức năng kỷ luật và một số thủ thuật bẫy gỡ lỗi để mô phỏng FUNCNAMEmảng bash .)

Câu trả lời chính tắc ở đây: http://mywiki.wooledge.org/BashFAQ/109 cũng cung cấp $-dưới dạng một chỉ báo khác (mặc dù không hoàn hảo) của trạng thái vỏ.


Ghi chú:

  • có thể tạo các hàm bash có tên "chính" và "nguồn" (ghi đè lên phần dựng sẵn ), các tên này có thể xuất hiện FUNCNAME[]nhưng miễn là chỉ mục cuối cùng trong mảng đó được kiểm tra thì không có sự mơ hồ.
  • Tôi không có câu trả lời hay cho pdksh. Điều gần nhất tôi có thể tìm thấy chỉ áp dụng cho pdksh, trong đó mỗi nguồn của tập lệnh sẽ mở một bộ mô tả tệp mới (bắt đầu bằng 10 cho tập lệnh gốc). Hầu như chắc chắn không phải thứ bạn muốn dựa vào ...

Làm thế nào ${FUNCNAME[(( ${#FUNCNAME[@]} - 1 ))]}để có được mục cuối cùng (dưới cùng) trong ngăn xếp? Sau đó, thử nghiệm đối với "chính" (phủ định cho OP) là đáng tin cậy nhất đối với tôi.
Adrian Günter

Nếu tôi có một PROMPT_COMMANDtập hợp, nó sẽ hiển thị như là chỉ mục cuối cùng của FUNCNAMEmảng nếu tôi chạy source sourcetest.sh. Đảo ngược kiểm tra (tìm kiếm mainnhư là chỉ số cuối cùng) có vẻ mạnh mẽ hơn : is_main() { [[ ${FUNCNAME[@]: -1} == "main" ]]; }.
dimo414

1
Các trạng thái trang man, FUNCNAMEchỉ có sẵn trong các chức năng. Theo thử nghiệm của tôi với declare -p FUNCNAME, bashhành xử khác nhau. v4.3 đưa ra lỗi bên ngoài các chức năng, trong khi v4.4 đưa ra declare -a FUNCNAME. Cả hai (!) Trả lại maincho ${FUNCNAME[0]}trong kịch bản chính (nếu nó được thực hiện) trong khi $FUNCNAMEcung cấp cho không có gì. Và: Có rất nhiều tập lệnh ngoài kia "ab" sử dụng các $BASH_SOURCEhàm bên ngoài, tôi nghi ngờ điều này có thể hoặc sẽ được thay đổi.
Tino

24

Lưu ý của biên tập viên: Giải pháp của câu trả lời này hoạt động mạnh mẽ, nhưng chỉ là bash. Nó có thể được sắp xếp hợp lý để
(return 2>/dev/null).

TL; DR

Cố gắng thực hiện một returntuyên bố. Nếu tập lệnh không có nguồn gốc, điều đó sẽ gây ra lỗi. Bạn có thể bắt lỗi đó và tiến hành khi bạn cần.

Đặt cái này trong một tập tin và gọi nó, nói, test.sh:

#!/usr/bin/env sh

# Try to execute a `return` statement,
# but do it in a sub-shell and catch the results.
# If this script isn't sourced, that will raise an error.
$(return >/dev/null 2>&1)

# What exit code did that give?
if [ "$?" -eq "0" ]
then
    echo "This script is sourced."
else
    echo "This script is not sourced."
fi

Thực hiện trực tiếp:

shell-prompt> sh test.sh
output: This script is not sourced.

Nguồn nó:

shell-prompt> source test.sh
output: This script is sourced.

Đối với tôi, điều này hoạt động trong zsh và bash.

Giải trình

Câu returnlệnh sẽ đưa ra một lỗi nếu bạn cố gắng thực thi nó bên ngoài hàm hoặc nếu tập lệnh không có nguồn gốc. Hãy thử điều này từ một dấu nhắc shell:

shell-prompt> return
output: ...can only `return` from a function or sourced script

Bạn không cần phải xem thông báo lỗi đó, vì vậy bạn có thể chuyển hướng đầu ra thành dev / null:

shell-prompt> return >/dev/null 2>&1

Bây giờ hãy kiểm tra mã thoát. 0 có nghĩa là OK (không có lỗi xảy ra), 1 có nghĩa là đã xảy ra lỗi:

shell-prompt> echo $?
output: 1

Bạn cũng muốn thực thi returncâu lệnh bên trong lớp vỏ phụ. Khi returntuyên bố chạy nó. . . tốt . . . trả lại. Nếu bạn thực thi nó trong shell phụ, nó sẽ trả về shell phụ đó, thay vì trả về script của bạn. Để thực hiện trong shell phụ, bọc nó trong $(...):

shell-prompt> $(return >/dev/null 2>$1)

Bây giờ, bạn có thể thấy mã thoát của shell phụ, phải là 1, vì một lỗi đã được đưa ra bên trong shell phụ:

shell-prompt> echo $?
output: 1

Điều này không thành công với tôi trong 0.5.8-2.1ubfox2 $ readlink $(which sh) dash $ . test.sh This script is sourced. $ ./test.sh This script is sourced.
Phil Rutschman

3
POSIX không chỉ định những gì returnnên làm ở cấp cao nhất ( pubs.opengroup.org/onlinepub/9699919799/utilities/ cảm ). Các dashxử lý vỏ một returnở cấp cao nhất như exit. Các shell khác thích bashhoặc zshkhông cho phép returnở cấp cao nhất, đó là tính năng một kỹ thuật như khai thác này.
dùng5754163

Nó hoạt động trong sh nếu bạn loại bỏ $trước lớp con. Đó là, sử dụng (return >/dev/null 2>&1)thay vì $(return >/dev/null 2>&1)- nhưng sau đó nó ngừng hoạt động trong bash.
Eponymous

@Eponymous: Chẳng hạn, vì giải pháp này không hoạt động , ví dụ dashnhư shtrên Ubuntu, giải pháp này thường không hoạt động sh. Giải pháp này phù hợp với tôi trong Bash 3.2.57 và 4.4.5 - có hoặc không có $trước đó (...)(mặc dù không bao giờ có lý do chính đáng cho việc này $).
mkuity0

2
returntrong khi một giá trị trả về explcit bị phá vỡ khi sourceing các tập lệnh ngay sau khi một lệnh bị lỗi. Đề xuất chỉnh sửa nâng cao.
DimG

12

FWIW, sau khi đọc tất cả các câu trả lời khác, tôi đã đưa ra giải pháp sau đây cho tôi:

Cập nhật: Trên thực tế, ai đó đã phát hiện ra một lỗi đã được sửa từ một câu trả lời khác cũng ảnh hưởng đến tôi. Tôi nghĩ rằng bản cập nhật ở đây cũng là một cải tiến (xem các chỉnh sửa nếu bạn tò mò).

Điều này hoạt động cho tất cả các tập lệnh, bắt đầu bằng#!/bin/bash nhưng có thể có nguồn gốc từ các shell khác nhau để tìm hiểu một số thông tin (như cài đặt) được giữ bên ngoài mainchức năng.

Theo các ý kiến ​​dưới đây, câu trả lời này ở đây dường như không hoạt động cho tất cả các bashbiến thể. Cũng không cho các hệ thống, nơi /bin/shdựa trên bash. IE nó không thành công cho bashv3.x trên MacOS. (Hiện tại tôi không biết làm thế nào để giải quyết điều này.)

#!/bin/bash

# Function definitions (API) and shell variables (constants) go here
# (This is what might be interesting for other shells, too.)

# this main() function is only meant to be meaningful for bash
main()
{
# The script's execution part goes here
}

BASH_SOURCE=".$0" # cannot be changed in bash
test ".$0" != ".$BASH_SOURCE" || main "$@"

Thay vì 2 dòng cuối cùng, bạn có thể sử dụng mã sau (theo ý kiến ​​của tôi ít đọc hơn) để không đặt BASH_SOURCEtrong các shell khác và cho phép set -ehoạt động trong main:

if ( BASH_SOURCE=".$0" && exec test ".$0" != ".$BASH_SOURCE" ); then :; else main "$@"; fi

Tập lệnh này có các thuộc tính sau:

  • Nếu được thực hiện theo cách bashthông thường, mainđược gọi. Xin lưu ý rằng điều này không bao gồm một cuộc gọi như bash -x script(nơi scriptkhông chứa đường dẫn), xem bên dưới.

  • Nếu có nguồn gốc bash, mainchỉ được gọi, nếu tập lệnh gọi xảy ra có cùng tên. (Ví dụ: nếu nó tự tìm nguồn hoặc thông qua bash -c 'someotherscript "$@"' main-script args..nơi main-scriptphải có, cái được testxem là $BASH_SOURCE).

  • Nếu nguồn gốc / thực thi / đọc / evaled bởi một vỏ khác bash, mainkhông được gọi ( BASH_SOURCEluôn luôn khác với $0).

  • mainkhông được gọi nếu bashđọc tập lệnh từ stdin, trừ khi bạn đặt thành $0chuỗi trống như vậy:( exec -a '' /bin/bash ) <script

  • Nếu đánh giá bằng bashvới eval ( eval "`cat script`" tất cả báo giá là quan trọng! ) Từ bên trong một số kịch bản khác, các cuộc gọi này main. Nếu evalđược chạy từ dòng lệnh trực tiếp, điều này tương tự như trường hợp trước, trong đó tập lệnh được đọc từ stdin. ( BASH_SOURCEtrống, trong khi $0thường là /bin/bashnếu không bị buộc phải làm một cái gì đó hoàn toàn khác.)

  • Nếu mainkhông được gọi, nó sẽ trả về true( $?=0).

  • Điều này không dựa trên hành vi bất ngờ (trước đây tôi đã viết không có giấy tờ, nhưng tôi không tìm thấy tài liệu nào mà bạn cũng không thể unsetthay đổi BASH_SOURCE):

    • BASH_SOURCElà một mảng dành riêng bash . Nhưng cho phép BASH_SOURCE=".$0"thay đổi nó sẽ mở ra một con giun rất nguy hiểm, vì vậy tôi dự đoán rằng điều này sẽ không có tác dụng (ngoại trừ, có lẽ, một số cảnh báo xấu xí xuất hiện trong một số phiên bản tương lai bash).
    • Không có tài liệu BASH_SOURCEhoạt động bên ngoài chức năng. Tuy nhiên, điều ngược lại (nó chỉ hoạt động trong các chức năng) không được ghi lại. Quan sát là, nó hoạt động (được thử nghiệm với bashv4.3 và v4.4, tiếc là tôi không còn bashv3.x nữa) và khá nhiều tập lệnh sẽ bị hỏng , nếu $BASH_SOURCEngừng hoạt động như quan sát. Do đó, kỳ vọng của tôi là, BASH_SOURCEvẫn như các phiên bản tương lai bash.
    • Ngược lại (tìm thấy tốt đẹp, BTW!) Xem xét ( return 0 ), đưa ra 0nếu có nguồn gốc và 1nếu không có nguồn gốc. Điều này có một chút bất ngờ không chỉ đối với tôi , và (theo các bài đọc ở đó) POSIX nói, rằng returntừ subshell là hành vi không xác định (và returnở đây rõ ràng là từ một subshell). Có lẽ tính năng này cuối cùng đã được sử dụng rộng rãi đến mức không thể thay đổi được nữa, nhưng AFAICS có nhiều khả năng một số bashphiên bản trong tương lai vô tình thay đổi hành vi trả lại trong trường hợp đó.
  • Thật không may bash -x script 1 2 3không chạy main. (So ​​sánh script 1 2 3nơi scriptkhông có đường dẫn). Sau đây có thể được sử dụng như cách giải quyết:

    • bash -x "`which script`" 1 2 3
    • bash -xc '. script' "`which script`" 1 2 3
    • Điều đó bash script 1 2 3không chạy maincó thể được coi là một tính năng.
  • Lưu ý rằng ( exec -a none script )các cuộc gọi main( bashkhông chuyển nó $0đến tập lệnh, vì điều này bạn cần sử dụng -cnhư được hiển thị ở điểm cuối cùng).

Vì vậy, ngoại trừ một số trường hợp góc, mainchỉ được gọi, khi tập lệnh được thực thi theo cách thông thường. Thông thường đây là, những gì bạn muốn, đặc biệt là vì nó thiếu mã phức tạp khó hiểu.

Lưu ý rằng nó rất giống với mã Python:

if __name__ == '__main__': main()

Điều này cũng ngăn chặn việc gọi main, ngoại trừ một số trường hợp góc, vì bạn có thể nhập / tải tập lệnh và thực thi điều đó__name__='__main__'

Tại sao tôi nghĩ rằng đây là một cách tổng quát tốt để giải quyết thách thức

Nếu bạn có một cái gì đó, có thể có nguồn gốc từ nhiều vỏ, nó phải tương thích. Tuy nhiên (đọc các câu trả lời khác), vì không có cách dễ dàng (dễ thực hiện) để phát hiện sourceing, bạn nên thay đổi các quy tắc .

Bằng cách thực thi rằng tập lệnh phải được thực thi bởi /bin/bash, bạn chính xác làm điều này.

Điều này giải quyết tất cả các trường hợp nhưng sau đó trong trường hợp đó tập lệnh không thể chạy trực tiếp:

  • /bin/bash không được cài đặt hoặc không hoạt động (i. E. trong môi trường khởi động)
  • Nếu bạn đặt nó vào một cái vỏ như trong curl https://example.com/script | $SHELL
  • (Lưu ý: Điều này chỉ đúng nếu bashgần đây của bạn . Công thức này được báo cáo là thất bại đối với một số biến thể nhất định. Vì vậy, hãy chắc chắn kiểm tra nó hoạt động cho trường hợp của bạn.)

Tuy nhiên tôi không thể nghĩ về bất kỳ lý do thực sự nào mà bạn cần điều đó và cả khả năng tìm nguồn kịch bản chính xác song song! Thông thường bạn có thể bọc nó để thực hiện mainbằng tay. Như thế

  • $SHELL -c '. script && main'
  • { curl https://example.com/script && echo && echo main; } | $SHELL
  • $SHELL -c 'eval "`curl https://example.com/script`" && main'
  • echo 'eval "`curl https://example.com/script`" && main' | $SHELL

Ghi chú

  • Câu trả lời này sẽ không thể thực hiện được nếu không có sự giúp đỡ của tất cả các câu trả lời khác! Ngay cả những cái sai - mà ban đầu làm cho tôi đăng bài này.

  • Cập nhật: Đã chỉnh sửa do những khám phá mới được tìm thấy trong https://stackoverflow.com/a/28776166/490291


Đã thử nghiệm cho ksh và bash-4.3. Đẹp. Thật đáng tiếc, câu trả lời của bạn sẽ có một cuộc sống khó khăn khi các câu trả lời khác đã có nhiều năm thu thập phiếu bầu.
hagello

cảm ơn vì câu trả lời này Tôi đánh giá cao bài kiểm tra dài hơn, "ít đọc hơn" với câu lệnh IF vì thật tốt khi xử lý cả hai tình huống để ít nhất đưa ra thất bại không im lặng. Trong trường hợp của tôi, tôi cần một tập lệnh có nguồn gốc hoặc thông báo cho người dùng về lỗi của họ khi không sử dụng nguồn.
Tim Richardson

@Tino: Đối với "cũng có thể có nguồn gốc từ các shell khác nhau": Trên macOS, nơi /bin/shcó hiệu quả bashtrong chế độ POSIX, chỉ định BASH_SOURCE phá vỡ tập lệnh của bạn. Trong vỏ khác ( dash, ksh, zsh), cách gọi kịch bản của bạn bằng cách đi qua nó như là một đối số tập tin trực tiếp đến việc thực thi shell trục trặc (ví dụ, zsh <your-script>sẽ làm cho kịch bản của bạn nhầm lẫn nghĩ rằng đó là nguồn gốc ). (Bạn đã đề cập rằng đường ống mã bị trục trặc, trong tất cả các shell.)
mkuity0

@Tino: Về một bên: Mặc dù . <your-script>(tìm nguồn cung ứng) hoạt động từ tất cả các shell giống như POSIX về nguyên tắc, nó chỉ có ý nghĩa nếu tập lệnh được viết rõ ràng chỉ sử dụng các tính năng POSIX, để ngăn các tính năng cụ thể cho một trình bao phá vỡ việc thực thi trong các vỏ khác; do đó sử dụng một dòng Bash shebang (chứ không phải #!/bin/sh) là khó hiểu - ít nhất là không có một bình luận dễ thấy. Ngược lại, nếu tập lệnh của bạn chỉ được chạy từ Bash (ngay cả khi chỉ xem xét các tính năng có thể không khả dụng), tốt hơn là từ chối thực thi trong các shell không phải Bash.
mkuity0

1
@ mkuity0 Cảm ơn một lần nữa, đã thêm một lưu ý rằng có vấn đề. Đối với những người đọc khác: Khi có nguồn gốc với bash v3.x thì không nên thực thi main, nhưng nó thực hiện điều này trong trường hợp này! Và khi có nguồn gốc /bin/sh, điều này bash --posixcũng xảy ra tương tự trong trường hợp này, và điều đó cũng hoàn toàn sai.
Tino

6

Điều này hoạt động sau này trong tập lệnh và không phụ thuộc vào biến _:

## Check to make sure it is not sourced:
Prog=myscript.sh
if [ $(basename $0) = $Prog ]; then
   exit 1  # not sourced
fi

hoặc là

[ $(basename $0) = $Prog ] && exit

1
Tôi nghĩ rằng câu trả lời này là một trong số ít POSIX tuân thủ ở đây. Với nhược điểm rõ ràng là bạn phải biết tên tệp và nó không hoạt động nếu cả hai tập lệnh có cùng tên tệp.
JepZ

5

Tôi sẽ đưa ra một câu trả lời cụ thể về BASH. Vỏ Korn, xin lỗi. Giả sử tên tập lệnh của bạn là include2.sh; sau đó tạo ra một chức năng bên trong các include2.shgọi am_I_sourced. Đây là phiên bản demo của tôi về include2.sh:

am_I_sourced()
{
  if [ "${FUNCNAME[1]}" = source ]; then
    if [ "$1" = -v ]; then
      echo "I am being sourced, this filename is ${BASH_SOURCE[0]} and my caller script/shell name was $0"
    fi
    return 0
  else
    if [ "$1" = -v ]; then
      echo "I am not being sourced, my script/shell name was $0"
    fi
    return 1
  fi
}

if am_I_sourced -v; then
  echo "Do something with sourced script"
else
  echo "Do something with executed script"
fi

Bây giờ hãy thử thực hiện nó theo nhiều cách:

~/toys/bash $ chmod a+x include2.sh

~/toys/bash $ ./include2.sh 
I am not being sourced, my script/shell name was ./include2.sh
Do something with executed script

~/toys/bash $ bash ./include2.sh 
I am not being sourced, my script/shell name was ./include2.sh
Do something with executed script

~/toys/bash $ . include2.sh
I am being sourced, this filename is include2.sh and my caller script/shell name was bash
Do something with sourced script

Vì vậy, điều này hoạt động mà không có ngoại lệ, và nó không sử dụng những $_thứ dễ vỡ . Thủ thuật này sử dụng tiện ích hướng nội của BASH, tức là các biến tích hợp FUNCNAMEBASH_SOURCE; xem tài liệu của họ trong trang hướng dẫn bash.

Chỉ có hai cảnh báo:

1) cuộc gọi am_I_called phải diễn ra trong tập lệnh có nguồn gốc, nhưng không nằm trong bất kỳ chức năng nào, kẻo ${FUNCNAME[1]}sẽ trả lại một cái gì đó khác. Vâng ... bạn có thể đã kiểm tra ${FUNCNAME[2]}- nhưng bạn chỉ làm cho cuộc sống của bạn khó khăn hơn.

2) chức năng am_I_called phải nằm trong tập lệnh có nguồn gốc nếu bạn muốn tìm hiểu tên của tập tin được bao gồm.


1
Làm rõ: Tính năng này yêu cầu BASH phiên bản 3+ để hoạt động. Trong BASH 2, FUNCNAME là biến vô hướng thay vì mảng. Ngoài ra BASH 2 không có biến mảng BASH_SOURCE.
Wi Girls Purwanto

4

Tôi muốn đề nghị một sự điều chỉnh nhỏ cho câu trả lời rất hữu ích của Dennis , để làm cho nó dễ mang theo hơn một chút, tôi hy vọng:

[ "$_" != "$0" ] && echo "Script is being sourced" || echo "Script is a subshell"

bởi vì [[không được công nhận bởi trình vỏ tương thích Debian POSIX (hơi khó giữ hậu môn) dash. Ngoài ra, người ta có thể cần các trích dẫn để bảo vệ chống lại tên tệp có chứa khoảng trắng, một lần nữa trong vỏ nói.


2

$_là khá giòn. Bạn phải kiểm tra nó như là điều đầu tiên bạn làm trong kịch bản. Và thậm chí sau đó, nó không được đảm bảo chứa tên của shell của bạn (nếu có nguồn gốc) hoặc tên của tập lệnh (nếu được thực thi).

Ví dụ: nếu người dùng đã đặt BASH_ENV, thì ở đầu tập lệnh, $_chứa tên của lệnh cuối cùng được thực thi trong BASH_ENVtập lệnh.

Cách tốt nhất tôi đã tìm thấy là sử dụng $0như thế này:

name="myscript.sh"

main()
{
    echo "Script was executed, running main..."
}

case "$0" in *$name)
    main "$@"
    ;;
esac

Thật không may, cách này không làm việc ra khỏi hộp trong zsh do sự functionargzerolựa chọn làm nhiều hơn tên gọi của nó, và là theo mặc định.

Để giải quyết vấn đề này, tôi đưa unsetopt functionargzerovào .zshenv.


1

Tôi theo mkuity0 biểu hiện nhỏ gọn .

Thật gọn gàng, nhưng tôi nhận thấy rằng nó có thể thất bại trong trường hợp ksh khi được gọi như sau:

/bin/ksh -c ./myscript.sh

(nó nghĩ rằng nó có nguồn gốc và không phải vì nó thực thi một lớp con) Nhưng biểu thức sẽ hoạt động để phát hiện điều này:

/bin/ksh ./myscript.sh

Ngoài ra, ngay cả khi biểu thức là nhỏ gọn, cú pháp không tương thích với tất cả các shell.

Vì vậy, tôi đã kết thúc với đoạn mã sau, hoạt động cho bash, zsh, dash và ksh

SOURCED=0
if [ -n "$ZSH_EVAL_CONTEXT" ]; then 
    [[ $ZSH_EVAL_CONTEXT =~ :file$ ]] && SOURCED=1
elif [ -n "$KSH_VERSION" ]; then
    [[ "$(cd $(dirname -- $0) && pwd -P)/$(basename -- $0)" != "$(cd $(dirname -- ${.sh.file}) && pwd -P)/$(basename -- ${.sh.file})" ]] && SOURCED=1
elif [ -n "$BASH_VERSION" ]; then
    [[ $0 != "$BASH_SOURCE" ]] && SOURCED=1
elif grep -q dash /proc/$$/cmdline; then
    case $0 in *dash*) SOURCED=1 ;; esac
fi

Hãy thêm hỗ trợ vỏ kỳ lạ :)


Trong ksh 93+u, ksh ./myscript.shhoạt động tốt cho tôi (với tuyên bố của tôi) - bạn đang sử dụng phiên bản nào?
mkuity0

Tôi e rằng không có cách nào đáng tin cậy để xác định xem tập lệnh có được sử dụng chỉ bằng các tính năng POSIX hay không: nỗ lực của bạn giả định Linux ( /proc/$$/cmdline) và chỉ tập trung vào dash( shví dụ , cũng hoạt động như trên Ubuntu). Nếu bạn sẵn sàng đưa ra một số giả định nhất định, bạn có thể kiểm tra $0thử nghiệm hợp lý - nhưng không đầy đủ - có thể mang theo được.
mkuity0

++ cho cách tiếp cận cơ bản, mặc dù vậy - Tôi đã tự do thích nghi với điều đó theo những gì tôi nghĩ là sự gần đúng di động tốt nhất của hỗ trợ sh/ dashcũng như, trong phần phụ lục cho câu trả lời của tôi.
mkuity0

0

Tôi không nghĩ có bất kỳ cách di động nào để làm điều này trong cả ksh và bash. Trong bash bạn có thể phát hiện nó bằng callerđầu ra, nhưng tôi không nghĩ có tồn tại tương đương trong ksh.


$0làm việc trong bash, ksh93pdksh. Tôi không phải ksh88kiểm tra.
Mikel

0

Tôi cần một lớp lót hoạt động trên [mac, linux] với bash.version> = 3 và không có câu trả lời nào trong số này phù hợp với dự luật.

[[ ${BASH_SOURCE[0]} = $0 ]] && main "$@"

1
Các bashgiải pháp hoạt động tốt (bạn có thể đơn giản hóa đến $BASH_SOURCE), nhưng kshgiải pháp không phải là mạnh mẽ: nếu kịch bản của bạn đang được nguồn gốc của kịch bản khác , bạn sẽ nhận được một dương tính giả.
mkuity0

0

Đi thẳng vào vấn đề: bạn phải đánh giá xem biến "$ 0" có bằng tên của Shell của bạn không.


Như thế này:

#!/bin/bash

echo "First Parameter: $0"
echo
if [[ "$0" == "bash" ]] ; then
    echo "The script was sourced."
else
    echo "The script WAS NOT sourced."
fi


Qua SHELL :

$ bash check_source.sh 
First Parameter: check_source.sh

The script WAS NOT sourced.

Qua NGUỒN :

$ source check_source.sh
First Parameter: bash

The script was sourced.



Thật khó để có một cách di động 100% để phát hiện xem tập lệnh có nguồn gốc hay không.

Về kinh nghiệm của tôi (7 năm với Shellscripting) , cách an toàn duy nhất (không phụ thuộc vào các biến môi trường với PID , v.v., không an toàn do thực tế đó là VARIABLE ), bạn nên:

  • mở rộng các khả năng từ nếu
  • sử dụng chuyển đổi / trường hợp, nếu bạn muốn.

Cả hai tùy chọn không thể tự động thu nhỏ, nhưng đó là cách an toàn hơn.



Ví dụ:

khi bạn tìm tập lệnh thông qua phiên SSH, giá trị được trả về bởi biến "$ 0" (khi sử dụng nguồn ), là -bash .

#!/bin/bash

echo "First Parameter: $0"
echo
if [[ "$0" == "bash" || "$0" == "-bash" ]] ; then
    echo "The script was sourced."
else
    echo "The script WAS NOT sourced."
fi

HOẶC LÀ

#!/bin/bash

echo "First Parameter: $0"
echo
if [[ "$0" == "bash" ]] ; then
    echo "The script was sourced."
elif [[ "$0" == "-bash" ]] ; then
    echo "The script was sourced via SSH session."
else
    echo "The script WAS NOT sourced."
fi

2
Downvote, vì điều này là hoàn toàn sai: /bin/bash -c '. ./check_source.sh'cho The script WAS NOT sourced.. Cùng một lỗi: ln -s /bin/bash pumuckl; ./pumuckl -c '. ./check_source.sh'->The script WAS NOT sourced.
Tino

2
Downvote của bạn đã thay đổi toàn bộ kịch bản và đóng góp lớn, Tino. Cảm ơn!
ivanleoncz

0

Tôi đã kết thúc với việc kiểm tra [[ $_ == "$(type -p "$0")" ]]

if [[ $_ == "$(type -p "$0")" ]]; then
    echo I am invoked from a sub shell
else
    echo I am invoked from a source command
fi

Khi sử dụng curl ... | bash -s -- ARGSđể chạy tập lệnh từ xa khi đang di chuyển, $ 0 sẽ chỉ bashthay vì bình thường /bin/bashkhi chạy tệp tập lệnh thực tế, vì vậy tôi sử dụng type -p "$0"để hiển thị đường dẫn bash đầy đủ.

kiểm tra:

curl -sSL https://github.com/jjqq2013/bash-scripts/raw/master/common/relpath | bash -s -- /a/b/c/d/e /a/b/CC/DD/EE

source <(curl -sSL https://github.com/jjqq2013/bash-scripts/raw/master/common/relpath)
relpath /a/b/c/d/e /a/b/CC/DD/EE

wget https://github.com/jjqq2013/bash-scripts/raw/master/common/relpath
chmod +x relpath
./relpath /a/b/c/d/e /a/b/CC/DD/EE

0

Đây là một câu trả lời từ một số câu trả lời khác, liên quan đến hỗ trợ vỏ chéo "phổ quát". Điều này được thừa nhận rất giống với https://stackoverflow.com/a/2942183/3220983 nói riêng, mặc dù hơi khác nhau. Điểm yếu của điều này là tập lệnh máy khách phải tôn trọng cách sử dụng nó (tức là bằng cách xuất một biến trước tiên). Điểm mạnh là điều này đơn giản và nên hoạt động "ở bất cứ đâu". Đây là một mẫu cho niềm vui cắt & dán của bạn:

# NOTE: This script may be used as a standalone executable, or callable library.
# To source this script, add the following *prior* to including it:
# export ENTRY_POINT="$0"

main()
{
    echo "Running in direct executable context!"
}

if [ -z "${ENTRY_POINT}" ]; then main "$@"; fi

Lưu ý: Tôi exportchỉ sử dụng để đảm bảo cơ chế này có thể được mở rộng thành các quy trình phụ.

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.