bashシェルスクリプトでの実行とソースの区別


22

私がここで求めているのは、非常に非正統的/型破り/危険であるか、または、Google-fuのスキルがちょうどいいものではありません...

bashシェルスクリプト、それは別のシェルスクリプトによってソース取得された場合に伝えるの方法のいずれかの簡単な存在である、またはそれ自体で実行されていますか?つまり、次の2つの動作を区別することはできますか?

# from another shell script
source myScript.sh

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

私が考えているのはbash、ソース化されたときに利用できる機能を含むユーティリティのようなシェルスクリプトを作成することです。ただし、このスクリプトが単独で実行されている場合は、定義された関数にも基づいて特定の操作を実行するようにします。このシェルスクリプトが取得できる環境変数のようなものはありますか、例えば

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

できれば、フラグ変数を設定するために呼び出し元スクリプトを必要としないソリューションを探しています。

編集:スクリプトのソースと実行の違い、使用されているスクリプトの違い(両方の方法で)を伝えることができるかどうかをここで見つけようとしていることを知っています。



@cuonglmは私の質問を編集しました、私は両方の違いを知っていますが、プログラムでシェルスクリプトにその違いを伝えることができるかどうか疑問に思っています。
hjk

4
@cuonglm:重複ではありません。彼は.コマンドについてはまったく質問していませんが、スクリプトがソースされているか、または正常に実行されているかどうか(つまり、サブシェルで)を検出することについてです。
ジャンダー

スタックオーバーフローでの同じ質問に対する非常に良い回答:stackoverflow.com/a/28776166/96944
Jannie Theunissen

回答:


19

はい-$ 0変数は、実行時のスクリプトの名前を示します。

$ 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

次のように実行されます:

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

これは、対話型シェルからのソースであることには対応していませんが、このアイデアは得られます(願っています)。

BASH_SOURCEを含むように更新-ありがとうhjk


十分近い。:)少なくともスクリプトの名前を指定する必要がある小さな障害ですが、他に実行可能な解決策がない場合はこの答えを取ります
...-hjk

7

@DarkHeartの答えを環境変数BASH_SOURCEと組み合わせると、うまくいくようです:

$ 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

編集BASH_SOURCEの配列内の要素の数を数えるだけなら、まだ簡単な解決策のようです:

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

1
'bash_source'変数を同時に見つけたようです。:)
DarkHeart

@DarkHeart私は答えに配​​列サイズもカウントする使用法を追加しました...このパスで私をほのめかしてくれてありがとう!:D
hjk

1

BusyBoxと同じように機能する同じ種類のライブラリスクリプトを作成しました。その中で、次の関数を使用して、ソースになっているかどうかをテストします...

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

Bashが管理するFUNCNAME配列は、本質的に関数呼び出しスタックです。$FUNCNAME(または${FUNCNAME[0]})は、現在実行中の関数の名前です。${FUNCNAME[1]}それを呼び出した関数の名前などです。

一番上の項目は、スクリプト自体の特別な値です。それが含まれます...

  • スクリプトがソースされている場合、「ソース」という言葉
  • スクリプトが実行され、テストが関数内から実行されている場合、「メイン」という言葉
  • ""(null)スクリプトが実行されており、テストが関数の外部で実行されている場合、つまり、スクリプト自体のレベルで実行されている場合。

上記の関数は、実際にはスクリプトレベルで呼び出された場合にのみ機能します(必要なのはこれだけです)。配列項目番号が間違っているため、別の関数内から呼び出された場合は失敗します。どこでも動作させるには、スタックの最上位を見つけてその値をテストする必要がありますが、これはより複雑です。

必要な場合は、「スタックの最上部」の配列項目番号を取得できます...

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

${#FUNCNAME[@]}配列内のアイテムの数です。ゼロベースの配列として、1を減算して最後のitem#を取得します。

これら3つの関数は、Pythonに似た関数スタックトレースを生成するために一緒に使用され、これらすべてがどのように機能するかをよりよく理解できる場合があります...

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
}

FUNCNAME、BASH_SOURCE、およびBASH_LINENOは、bashによって1つの3次元配列であるかのように維持される3つの配列であることに注意してください。


0

配列のカウントは信頼できないように見えsource、ドット(.)の使用も非常に一般的であるため(sourceキーワードよりも前に)使用されたとは思わないことを付け加えてください。

たとえば、次sourced.shのみを含むスクリプトの場合echo $0


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

比較ソリューションは、より良い動作を示唆しました。


0

対話型シェルからソースを取得するときにも機能する1つの方法:

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

BASH_LINENO変数は、関数呼び出しがで実行されたすべての行を有するアレイです。スクリプトを直接呼び出す場合はゼロ、または行番号に対応する整数になります。

BASH_ *変数のドキュメント

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.