bashシェルスクリプトで実行コードとソースコードを区別しますか?

bashシェルスクリプトで実行コードとソースコードを区別しますか?

私がここに尋ねるのは、非常に不慣れである、またはフレームワークに縛られていない、危険である、または私のGoogle技術が単にレベルに達していないことです。

シェルスクリプトで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

好ましくは、フラグ変数を設定するために呼び出し側スクリプトを必要としない解決策を探している。

編集する:スクリプトを受け取るものとスクリプトを実行することの違いを知っています。可能であれば、ここで何を知りたいです。違いを教えてください使用されるスクリプトでは(2つの方法で)。

答え1

はい - $ 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を含めるように更新されました。ありがとうございます。

答え2

コンビネーション@シム環境変数を使用して応答することは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

答え3

私はBusyBoxに似た同じタイプのライブラリスクリプトを作成しました。ここでは、次の関数を使用してインポート中かどうかをテストします。

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

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

親項目はスクリプト自体の特殊値です。それは含まれています...

  • スクリプトがソースの場合は、「source」という単語が表示されます。
  • スクリプトが実行され、テストが関数内で実行される場合、「main」という単語
  • "" (null) スクリプトが実行され、テストが機能外、つまりスクリプト自体のレベルで実行される場合。

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

必要に応じて、次のコマンドを使用して「スタックの一番上に」配列項目番号を取得できます。

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

${#FUNCNAME[@]}配列内の項目の数。 0 から始まる配列に 1 を引いて、最後の項目 # を取得します。

これら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が3次元配列のように維持する3つの配列であることに注意してください。

答え4

source計算配列は信頼できないようで、dot()を使用すること.も一般的であり、sourceキーワードよりも優先するので、dot()を使用すると仮定してはならないことを追加したかったです。

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


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

提案された比較ソリューションはよりうまく機能します。

関連情報