Phân biệt giữa chạy và được lấy nguồn trong tập lệnh bash shell?


22

Hoặc những gì tôi đang hỏi ở đây là cực kỳ không chính thống / không thông thường / rủi ro, hoặc các kỹ năng Google-fu của tôi không phải là để ...

Trong một bashtập lệnh shell, có cách nào dễ dàng để biết liệu nó có được lấy từ một tập lệnh shell khác không, hay nó đang được điều hành bởi chính nó? Nói cách khác, có thể phân biệt giữa hai hành vi sau đây không?

# from another shell script
source myScript.sh

# from command prompt, or another shell script
./myScript.sh

Những gì tôi nghĩ làm là tạo ra một kịch bản shell giống như tiện ích có chứa các bashhàm có thể được cung cấp khi có nguồn gốc. Khi tập lệnh này tự chạy, tôi sẽ thích nó thực hiện một số thao tác nhất định, dựa trên các chức năng được xác định. Có một loại biến môi trường nào mà tập lệnh shell này có thể nhận được, vd

some_function() {
    # ...
}
if [ -z "$IS_SOURCED" ]; then
    some_function;
fi

Tốt hơn là, tôi đang tìm một giải pháp không yêu cầu tập lệnh người gọi để đặt bất kỳ biến cờ nào.

chỉnh sửa : Tôi biết sự khác biệt giữa tìm nguồn cung ứng và chạy tập lệnh, những gì tôi đang cố gắng tìm hiểu ở đây nếu có thể cho biết sự khác biệt trong tập lệnh đang được sử dụng (theo cả hai cách).



@cuonglm đã chỉnh sửa câu hỏi của tôi, tôi biết sự khác biệt giữa cả hai nhưng tôi tự hỏi liệu tôi có thể lập trình để tạo kịch bản shell cho sự khác biệt không.
hjk

4
@cuonglm: Không trùng lặp. Anh ta không hỏi về .lệnh nào cả, nhưng về việc phát hiện xem một tập lệnh đã có nguồn gốc hay chạy bình thường (tức là trong một khung con).
Jander

Câu trả lời rất hay cho cùng một câu hỏi tại stack overflow: stackoverflow.com/a/28776166/96944
Jannie Theunissen

Câu trả lời:


19

Có - biến $ 0 cho biết tên của tập lệnh khi nó được chạy:

$ cat example.sh
#!/bin/bash
script_name=$( basename ${0#-} ) #- needed if sourced no path
this_script=$( basename ${BASH_SOURCE} )
if [[ ${script_name} = ${this_script} ]] ; then
    echo "running me directly"
else
    echo "sourced from ${script_name}"
fi 

$ cat example2.sh
#!/bin/bash
. ./example.sh

Mà chạy như:

$ ./example.sh
running me directly
$ ./example2.sh
example.sh sourced from example2.sh

Điều đó không phục vụ cho việc lấy nguồn từ vỏ tương tác, nhưng bạn có ý tưởng này (tôi hy vọng).

Đã cập nhật để bao gồm BASH_SOURCE - cảm ơn hjk


Đủ gần. :) Trở ngại nhỏ mà tôi sẽ cần chỉ định ít nhất tên của tập lệnh, nhưng tôi sẽ nhận câu trả lời này nếu không có giải pháp khả thi nào khác ...
hjk

7

Kết hợp câu trả lời của @ DarkHeart với biến môi trường BASH_SOURCEdường như là một mẹo nhỏ:

$ head example*.sh
==> example2.sh <==
#!/bin/bash
. ./example.sh

==> example.sh <==
#!/bin/bash
if [ "$(basename $0)" = "$(basename $BASH_SOURCE)" ]; then
    echo "running directly"
else
    echo "sourced from $0"
fi
$ ./example2.sh
sourced from ./example2.sh
$ ./example.sh
running directly

chỉnh sửa Có vẻ là một giải pháp đơn giản hơn nếu tôi chỉ đếm số phần tử trong BASH_SOURCEmảng:

if [ ${#BASH_SOURCE[@]} -eq 1 ]; then echo "running directly"; else echo "sourced from $0"; fi

1
Có vẻ như chúng tôi đã tìm thấy biến 'bash_source' cùng một lúc. :)
DarkHeart

@DarkHeart Tôi đã thêm vào câu trả lời của mình về cách sử dụng đếm kích thước mảng ... cảm ơn vì đã gợi ý cho tôi về con đường này! : D
hjk

1

Tôi vừa tạo cùng một loại kịch bản thư viện hoạt động rất nhiều như BusyBox. Trong đó, tôi sử dụng chức năng sau để kiểm tra xem nó có nguồn gốc không ...

function isSourced () {
  [[ "${FUNCNAME[1]}" == "source" ]]  && return 0
  return 1
}

Mảng FUNCNAME do Bash duy trì thực chất là một ngăn xếp cuộc gọi hàm. $FUNCNAME(hoặc ${FUNCNAME[0]}) là tên của chức năng hiện đang thực thi. ${FUNCNAME[1]}là tên của hàm gọi nó, v.v.

Mục trên cùng là một giá trị đặc biệt cho chính tập lệnh. Nó sẽ chứa ...

  • từ "nguồn" nếu tập lệnh đang được lấy nguồn
  • từ "chính" nếu tập lệnh đang được thực thi VÀ kiểm tra đang được thực hiện từ bên trong một hàm
  • "" (Null) nếu tập lệnh đang được thực thi VÀ kiểm tra đang được thực hiện bên ngoài bất kỳ chức năng nào, đó là ... ở cấp độ của chính tập lệnh.

Hàm trên thực sự chỉ hoạt động khi được gọi ở cấp tập lệnh (đó là tất cả những gì tôi cần). Nó sẽ thất bại nếu được gọi từ bên trong một hàm khác vì số mục mảng sẽ bị sai. Để làm cho nó hoạt động ở bất cứ đâu đòi hỏi phải tìm đỉnh của ngăn xếp và kiểm tra giá trị đó, điều này phức tạp hơn.

Nếu bạn cần điều đó, bạn có thể lấy số mục mảng của "đỉnh của ngăn xếp" với ...

  local _top_of_stack=$(( ${#FUNCNAME[@]} - 1 ))

${#FUNCNAME[@]}là số lượng các mục trong mảng. Là một mảng dựa trên zero, chúng tôi trừ đi 1 để có được mục cuối cùng #.

Ba hàm này được sử dụng cùng nhau để tạo ra dấu vết ngăn xếp hàm tương tự như Python và chúng có thể cho bạn ý tưởng tốt hơn về cách tất cả các chức năng này hoạt động ...

function inspFnStack () {
  local T+="  "
  local _at=
  local _text="\n"
  local _top=$(inspFnStackTop)
  local _fn=${FUNCNAME[1]}; [[ $_fn =~ source|main ]]  || _fn+="()"
  local i=_top; ((--i))
  #
  _text+="$i item function call stack for $_fn ...\n"
  _text+="| L   BASH_SOURCE{BASH_LINENO called from}.FUNCNAME  \n"
  _text+="| ---------------------------------------------------\n"
  while (( $i > 0 ))
  do
    _text+="| $i ${T}$(inspFnStackItem $i)\n"
    T+="  "
    ((--i))
  done
  #
  printf "$_text\n"
  #
  return 0
}

function inspFnStackItem ()  {
  local _i=$1
  local _fn=${FUNCNAME[$_i]}; [[ $_fn =~ source|main ]]  || _fn+="()"
  local _at="${BASH_LINENO[$_i-1]}"; [[ $_at == 1 ]]  && _at="trap"
  local _item="${BASH_SOURCE[$_i]}{${_at}}.$_fn"
  #
  printf "%s" "$_item"
  return 0
}

function inspFnStackTop () {
  # top stack item is 1 less than length of FUNCNAME array stack
  printf "%d\n" $(( ${#FUNCNAME[@]} - 1 ))
  #
  return 0
}

Lưu ý rằng FUNCNAME, BASH_SOURCE và BASH_LINENO là 3 mảng được duy trì bởi bash như thể chúng là một mảng ba chiều.


0

Chỉ muốn thêm rằng việc đếm mảng dường như không đáng tin cậy và có lẽ người ta không nên cho rằng sourceđã được sử dụng vì sử dụng dấu chấm ( .) cũng rất phổ biến (và có trước sourcetừ khóa).

Ví dụ: đối với sourced.shtập lệnh chỉ chứa echo $0:


$ . sourced.sh 
bash
$ source sourced.sh 
bash
$ chmod +x sourced.sh 
$ ./sourced.sh 
./sourced.sh
$ cat ./sourced.sh 
echo $0

Các giải pháp so sánh đề xuất làm việc tốt hơn.


0

Một cách cũng hoạt động khi tìm nguồn cung ứng từ vỏ tương tác :

if [ $BASH_LINENO -ne 0 ]; then
    some_function;
fi

Các BASH_LINENObiến cũng là một mảng với tất cả các dòng chức năng gọi được thực hiện tại. Sẽ là 0 nếu bạn gọi trực tiếp tập lệnh hoặc số nguyên tương ứng với số dòng.

Các tài liệu biến BASH_ *

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.