exit
ソースが提供されている場合に呼び出されないようにするスクリプトがあります。
$0 == bash
スクリプトを別のスクリプトから取得した場合、またはユーザーがのような別のシェルから取得した場合に問題があるかどうかを確認することを考えましたksh
。
スクリプトが供給されているかどうかを検出する信頼できる方法はありますか?
exit
ソースが提供されている場合に呼び出されないようにするスクリプトがあります。
$0 == bash
スクリプトを別のスクリプトから取得した場合、またはユーザーがのような別のシェルから取得した場合に問題があるかどうかを確認することを考えましたksh
。
スクリプトが供給されているかどうかを検出する信頼できる方法はありますか?
回答:
これはBashとKornの間で移植できるようです:
[[ $_ != $0 ]] && echo "Script is being sourced" || echo "Script is a subshell"
これと同様の行またはpathname="$_"
(後のテストとアクションを使用した)のような割り当ては、スクリプトの最初の行またはシバンの後の行に使用する必要があります(使用する場合は、kshで使用する必要があります)ほとんどの状況)。
BASH_ENV
、$_
スクリプトの先頭にから実行する最後のコマンドとなりますBASH_ENV
。
$_
は問題です。
bash script
(シェル実行可能ファイルを介した呼び出し、このソリューションはソースとして誤って報告します)、および(b)(はるかに可能性が低い)echo bash; . script
($_
シェルがスクリプトをソースしていると一致する場合、このソリューションは誤って報告しますサブシェル)。唯一のシェル固有の特殊変数(例えば、$BASH_SOURCE
)堅牢なソリューションを(強固なPOSIX準拠のソリューションが存在しないということになる)ことができます。面倒ではありますが、堅牢なクロスシェルテストを作成することは可能です。
BashのバージョンがBASH_SOURCE配列変数を認識している場合は、次のようにしてください。
# 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 ..."
${BASH_SOURCE[0]}
代わりに使用する理由はあり$BASH_SOURCE
ますか?そして${0}
vs $0
?
$array
、${array[0]}
デフォルトで取得されます。それで、もう一度、理由があります[...]?
以下のための堅牢なソリューションをbash
、ksh
、zsh
を含む、クロスシェル 1、プラス合理的に堅牢なPOSIX準拠のソリューション:
与えられたバージョン番号は、機能が検証されたものです -おそらく、これらのソリューションは以前のバージョンでも動作します- フィードバックを歓迎します。
使い方POSIX機能のみ(などのようdash
として機能する、 /bin/sh
Ubuntuの上で)、ありません何の堅牢な方法スクリプトが調達されているかどうかを決定するためには-最高のために以下を参照してください近似。
ワンライナーが続きます-以下の説明; クロスシェルバージョンは複雑ですが、堅牢に動作するはずです。
bash(3.57および4.4.19で検証済み)
(return 0 2>/dev/null) && sourced=1 || sourced=0
ksh(93u +で検証済み)
[[ $(cd "$(dirname -- "$0")" &&
printf '%s' "${PWD%/}/")$(basename -- "$0") != "${.sh.file}" ]] &&
sourced=1 || sourced=0
zsh(5.0.5で検証済み)- 関数の外で呼び出してください
[[ $ZSH_EVAL_CONTEXT =~ :file$ ]] && sourced=1 || sourced=0
クロスシェル(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
POSIX準拠 ; ないワンライナー(単一のパイプライン)技術的な理由とのためではない、完全に堅牢(下を参照してください):
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
説明:
(return 0 2>/dev/null) && sourced=1 || sourced=0
注:この手法は、元のソリューションよりも堅牢であることが判明したため、user5754163の回答から適応されました[[ $0 != "$BASH_SOURCE" ]] && sourced=1 || sourced=0
[1]。
Bashはreturn
関数からのステートメントのみを許可し、スクリプトのトップレベルのスコープでは、スクリプトがソースである場合にのみ許可します。
return
ていないスクリプトのトップレベルのスコープでが使用されている場合、エラーメッセージが発行され、終了コードがに設定され1
ます。(return 0 2>/dev/null)
サブシェルで実行return
し、エラーメッセージを抑制します。その後、終了コードは、スクリプトのソース()かそうでないか()を示します。これは、および演算子と共に使用され、それに応じて変数を設定します。0
1
&&
||
sourced
return
ソーススクリプトのトップレベルのスコープで実行するとスクリプトが終了するため、サブシェルを使用する必要があります。0
return
return [N]
return
[[ \
$(cd "$(dirname -- "$0")" && printf '%s' "${PWD%/}/")$(basename -- "$0") != \
"${.sh.file}" \
]] &&
sourced=1 || sourced=0
特殊変数${.sh.file}
はにいくらか似てい$BASH_SOURCE
ます。はbash、zsh、およびdashで構文エラーを${.sh.file}
引き起こすので、マルチシェルスクリプトで条件付きで実行してください。
bashのとは異なり、$0
かつ${.sh.file}
であることが保証されない正確として、非ソースの場合は同じ$0
かもしれ相対パスながら、${.sh.file}
常に完全なパスので、$0
比較する前に完全なパスに解決されなければなりません。
[[ $ZSH_EVAL_CONTEXT =~ :file$ ]] && sourced=1 || sourced=0
$ZSH_EVAL_CONTEXT
評価コンテキストに関する情報が含まれています-関数の外で呼び出します。ソーススクリプト[のトップレベルスコープ]内で、で$ZSH_EVAL_CONTEXT
終了し:file
ます。
警告:コマンド置換の内部では、zshがを追加:cmdsubst
するので、そこをテスト$ZSH_EVAL_CONTEXT
し :file:cmdsubst$
てください。
スクリプトを実行している可能性のあるシェルのバイナリファイル名を知っていることに基づいて、特定の仮定をすることをいとわない場合は、合理的ではありますが、スクリプトが供給されているかどうかについては間違いのない推測を行うことができます。
特に、これは、スクリプトが別のスクリプトによって供給されている場合、このアプローチが失敗することを意味します。
この回答の「ソース化された呼び出しを処理する方法」のセクションでは、POSIX機能では処理できないエッジケースについて詳しく説明しています。
これは、の標準的な動作に依存して$0
、zsh
インスタンスがないため、ない呈します。
したがって、最も安全なアプローチは、上記の堅牢なシェル固有のメソッドを、残りのすべてのシェルのフォールバックソリューションと組み合わせることです。
帽子の先端ステファンDesneuxと彼の答えのインスピレーションのため(に私のクロスシェル文の表現を変換sh
互換if
の文や他のシェルのハンドラを追加します)。
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は、単にファイル名をバイナリに渡して、に[[ $0 != "$BASH_SOURCE" ]]
あるスクリプトを実行すると、誤検知を引き起こすことを発見しました。例えば、、理由だけで、その後であるのに対し、あるフルパス。あなたが通常でスクリプトを呼び出すために、この技術を使用していないだろうが-あなたはちょうどそれら呼び出すと思い、直接(それは- )であると組み合わせる際に役立つためのデバッグ。$PATH
bash
bash my-script
$0
my-script
$BASH_SOURCE
$PATH
my-script
-x
@DennisWilliamsonの回答を読んだ後、いくつかの問題があります。以下を参照してください。
この質問は ksh そして バッシュ、この回答には別の部分があります ksh... 下記参照。
[ "$0" = "$BASH_SOURCE" ]
試してみましょう(そのbashが可能なため、その場で;-):
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)
私は読みやすさのためにsource
代わりにoff を使用しています.
(.
のエイリアスとして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)
プロセスがソースのままである間、プロセス番号は変更されないことに注意してください。
echo $$
29301
$_ == $0
比較を使用しない理由多くのケースを確実にするために、私は本当のスクリプトを書き始めます:
#!/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)"
これを次のファイルにコピーしますtestscript
。
cat >testscript
chmod +x testscript
これでテストできます:
./testscript
proc: 25758[ppid:24890] is own (DW purpose: subshell)
それで大丈夫です。
. ./testscript
proc: 24890[ppid:24885] is sourced (DW purpose: sourced)
source ./testscript
proc: 24890[ppid:24885] is sourced (DW purpose: sourced)
それで大丈夫です。
ただし、-x
フラグを追加する前にスクリプトをテストする場合:
bash ./testscript
proc: 25776[ppid:24890] is own (DW purpose: sourced)
または、事前定義された変数を使用するには:
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)
これはもう機能しません。
コメントを5行目から6行目に移動すると、より読みやすい回答が得られます。
./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
使わないので ksh 多くの場合、manページを読んだ後、私の試みがあります:
#!/bin/ksh
set >/tmp/ksh-$$.log
これを次の場所にコピーしますtestfile.ksh
。
cat >testfile.ksh
chmod +x testfile.ksh
それを2回実行するより:
./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
そして見る:
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
ソース実行で継承された変数がいくつかありますが、実際には何も関連していません...
あなたはそれ$SECONDS
がに近いことを確認することもできますが、それは手動でソースされたケース0.000
のみを確実にします...
親が何であるかを確認することもできます:
これをあなたの中に入れてくださいtestfile.ksh
:
ps $PPID
より:
./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
またはps ho cmd $PPID
、ただし、これは1レベルのサブセッションでのみ機能します...
申し訳ありませんが、信頼できる方法を見つけることができませんでした。 ksh。
[ "$0" = "$BASH_SOURCE" ] || [ -z "$BASH_SOURCE" ]
パイプ経由で読み込まれるスクリプト(cat script | bash
)
.
エイリアスではないことに注意してくださいsource
。実際にはその逆です。source somescript.sh
Bash-ismであり、移植性はありません. somescript.sh
。POSIXと移植性のあるIIRCです。
BASH_SOURCE[]
答(後のbash-3.0)、最も簡単なようだが、しかしBASH_SOURCE[]
れる関数本体の外に仕事に文書化されていません(それが現在のmanページと不一致で、仕事に起こります)。
Wirawan Purwantoによって提案されている最も堅牢な方法はFUNCNAME[1]
、関数内でチェックすることです。
function mycheck() { declare -p FUNCNAME; }
mycheck
次に:
$ bash sourcetest.sh
declare -a FUNCNAME='([0]="mycheck" [1]="main")'
$ . sourcetest.sh
declare -a FUNCNAME='([0]="mycheck" [1]="source")'
これは、の出力とcaller
値をチェックして、呼び出し元のコンテキストmain
をsource
区別することと同じです。を使用FUNCNAME[]
すると、caller
出力をキャプチャして解析する手間が省けます。ただし、ローカルコールの深さを把握または計算して正確にする必要があります。スクリプトが別の関数またはスクリプト内から供給されるような場合は、配列(スタック)がより深くなります。(FUNCNAME
は特別なbash配列変数であり、それが決してない限り、呼び出しスタックに対応する連続したインデックスを持つ必要がありますunset
。)
function issourced() {
[[ ${FUNCNAME[@]: -1} == "source" ]]
}
(bash-4.2以降${FUNCNAME[-1]}
では、配列の最後の項目の代わりに、より単純なフォームを使用できます。以下のDennis Williamsonのコメントのおかげで、改善および簡略化されています。)
ただし、述べられているようにあなたの問題は「私はそれが供給されている場合、「exit」を呼び出さないスクリプトを持っています」です。bash
この状況の一般的なイディオムは次のとおりです。
return 2>/dev/null || exit
スクリプトが読み込まれている場合、return
は読み込まれたスクリプトを終了し、呼び出し元に戻ります。
スクリプトが実行されている場合、return
エラーが返され(リダイレクトされ)、exit
スクリプトは通常どおり終了します。両方ともreturn
、exit
必要に応じて終了コードを取得できます。
悲しいことに、これは機能しませんksh
(少なくとも、私が持っているAT&T派生バージョンでは機能しません)。これは、関数またはドットソーススクリプトの外部で呼び出された場合return
と同等に扱われexit
ます。
更新:の最新バージョンでできることは、関数呼び出しの深さに設定されてksh
いる特殊変数をチェックすること.sh.level
です。呼び出されたスクリプトの場合、これは最初は設定されておらず、ドットソースのスクリプトの場合は1に設定されます。
function issourced {
[[ ${.sh.level} -eq 2 ]]
}
issourced && echo this script is sourced
これはbashのバージョンほど堅牢ではありません。issourced()
テストするファイルの最上位または既知の関数の深さで呼び出す必要があります。
(また、規律関数といくつかのデバッグトラップトリックを使用してbash 配列をエミュレートするgithubのこのコードに興味があるかもしれません。)ksh
FUNCNAME
ここでの正解:http : //mywiki.wooledge.org/BashFAQ/109$-
は、シェルの状態の(不完全ではありますが)別の指標としても提供しています。
ノート:
FUNCNAME[]
ますが、その配列の最後の項目のみがテストされている限り、あいまいさはありません。pdksh
。私が見つけることができる最も近いものはにのみ適用されpdksh
ます。スクリプトをソースするたびに、新しいファイル記述子が開きます(元のスクリプトは10から始まります)。ほとんどの場合、信頼したいものではありません...${FUNCNAME[(( ${#FUNCNAME[@]} - 1 ))]}
スタックの最後(下)のアイテムを取得するにはどうすればよいですか?次に、「メイン」(OPの否定)に対するテストが、私にとって最も信頼できるものでした。
PROMPT_COMMAND
セットがあるFUNCNAME
場合、を実行すると、配列の最後のインデックスとして表示されますsource sourcetest.sh
。チェックを反転させる(main
最後のインデックスとして探す)と、より堅牢に見えますis_main() { [[ ${FUNCNAME[@]: -1} == "main" ]]; }
。
編集者注:この回答の解決策はbash
確実に機能しますが、それだけです。それはに合理化することができます
(return 2>/dev/null)
。
TL; DR
return
ステートメントを実行してみてください。スクリプトが提供されていない場合、エラーが発生します。そのエラーをキャッチして、必要に応じて続行できます。
これをファイルに入れて、たとえば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
直接実行します。
shell-prompt> sh test.sh
output: This script is not sourced.
ソース:
shell-prompt> source test.sh
output: This script is sourced.
私にとって、これはzshとbashで動作します。
説明
return
あなたは関数の外やスクリプトが供給されていない場合は、それを実行しようとした場合のステートメントは、エラーが発生します。シェルプロンプトからこれを試してください:
shell-prompt> return
output: ...can only `return` from a function or sourced script
そのエラーメッセージを表示する必要はないので、出力をdev / nullにリダイレクトできます。
shell-prompt> return >/dev/null 2>&1
次に、終了コードを確認します。0はOK(エラーは発生していない)を意味し、1はエラーが発生したことを意味します。
shell-prompt> echo $?
output: 1
また、return
サブシェル内でステートメントを実行したいとします。return
ステートメントが実行するとき。。。上手 。。。戻り値。サブシェルで実行すると、スクリプトから戻るのではなく、そのサブシェルから戻ります。サブシェルで実行するには、次のようにラップし$(...)
ます。
shell-prompt> $(return >/dev/null 2>$1)
これで、サブシェルの内部でエラーが発生したため、サブシェルの終了コードが1であることがわかります。
shell-prompt> echo $?
output: 1
$ readlink $(which sh)
dash
$ . test.sh
This script is sourced.
$ ./test.sh
This script is sourced.
return
トップレベルで何をすべきかを指定していません(pubs.opengroup.org/onlinepubs/9699919799/utilities/…)。dash
シェル扱いreturn
としてトップレベルexit
。以下のような他のシェルbash
かはzsh
許可されていませんreturn
。このような悪用技術特徴である、トップレベルで。
$
サブシェルの前を削除すると、shで動作します。つまり、の(return >/dev/null 2>&1)
代わりにを使用しますが、$(return >/dev/null 2>&1)
bashで機能しなくなります。
dash
、この解決策は仕事は、として機能しない場合sh
のUbuntu上で、例えば、この溶液はない一般的と連携しますsh
。このソリューションは、Bash 3.2.57および4.4.5で私にとっては機能します- $
以前のバージョンの有無に(...)
かかわらず(ただし、の正当な理由は決してありません$
)。
return
source
不正に終了したコマンドの直後にスクリプトを実行すると、明示的な戻り値を使用しないと壊れます。拡張編集を提案しました。
FWIW、他のすべての答えを読んだ後、私は次の解決策を思いついた:
更新:実際に、誰かが私に影響を与えた別の回答に後で修正されたエラーを見つけました。ここでの更新も改善だと思います(興味があれば編集を参照してください)。
これはすべてのスクリプトで機能します。スクリプトは、最初#!/bin/bash
はmain
機能しますが、関数の外部で保持されるいくつかの情報(設定など)を学習するために、別のシェルによって提供される場合もあります。
以下のコメントによると、この回答は明らかにすべての
bash
バリアントで機能するわけではありません。また、/bin/sh
はに基づいてbash
いるシステムにも当てはまりません。IEbash
はMacOSのv3.xでは失敗します。(現在、私はこれを解決する方法がわかりません。)
#!/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 "$@"
最後の2行の代わりに、次の(私の意見では読みにくい)コードをBASH_SOURCE
使用set -e
して、他のシェルで設定しないで、で作業できるようにすることができmain
ます。
if ( BASH_SOURCE=".$0" && exec test ".$0" != ".$BASH_SOURCE" ); then :; else main "$@"; fi
このスクリプトレシピには、次のプロパティがあります。
bash
通常の方法で実行された場合、main
呼び出されます。これにはbash -x script
(script
パスが含まれない)などの呼び出しは含まれません。以下を参照してください。
供給した場合bash
、main
呼び出し元のスクリプトが同じ名前を持つように発生した場合にのみ、と呼ばれています。(たとえば、それ自体がソースである場合、またはbash -c 'someotherscript "$@"' main-script args..
どこにあるmain-script
必要があるかによって、とtest
見なされるもの$BASH_SOURCE
)。
ソースの場合/ /読み込み/実行eval
以外のシェルによって編bash
、main
呼び出されません(BASH_SOURCE
常にとは異なります$0
)。
main
次のように空の文字列にbash
設定$0
しない限り、標準入力からスクリプトを読み取る場合は呼び出されません。( exec -a '' /bin/bash ) <script
他のスクリプト内からbash
with eval
(eval "`cat script`"
すべての引用符が重要です!)によって評価された場合、これはを呼び出しますmain
。eval
をコマンドラインから直接実行する場合、これは前のケースと同様であり、スクリプトはstdinから読み取られます。(BASH_SOURCE
は空白ですが、$0
通常は/bin/bash
完全に異なるものに強制されていません。)
main
呼び出されない場合は、戻りますtrue
($?=0
)。
これは予期しない動作に依存しません(以前はドキュメント化されていませんunset
でしたがBASH_SOURCE
、どちらも変更できないドキュメントは見つかりませんでした)。
BASH_SOURCE
bashの予約済み配列です。しかし、BASH_SOURCE=".$0"
変更を許可すると、非常に危険なワームの缶が開かれるため、これは効果がないはずです(おそらく、いくつかの醜い警告がの将来のバージョンで表示されることを除いてbash
)。BASH_SOURCE
関数の外部で機能するドキュメントはありません。ただし、その反対(関数でのみ機能する)は文書化されていません。観察結果は、それが機能すること(bash
v4.3とv4.4でテスト済み、残念ながらbash
もうv3.xがない)であり、$BASH_SOURCE
観察されたように機能しなくなった場合、非常に多くのスクリプトが壊れることになります。したがって、私の期待は、のBASH_SOURCE
将来のバージョンでbash
もそのままです。( return 0 )
。これにより0
、ソースが提供されている1
かどうかがわかります。 これは私にとってだけでなく、予想外のことです。(そこでの読みによると)POSIXによると、return
サブシェルからの動作は未定義です(そして、return
ここは明らかにサブシェルからのものです)。おそらく、この機能は最終的には十分に広く使用されるようになり、変更できなくなりますが、AFAICSの場合、将来のbash
バージョンによっては、誤って戻り動作が変更される可能性が高くなります。残念ながらbash -x script 1 2 3
動作しませんmain
。(パスがないscript 1 2 3
場所script
を比較してください)。以下は回避策として使用できます。
bash -x "`which script`" 1 2 3
bash -xc '. script' "`which script`" 1 2 3
bash script 1 2 3
実行されませんmain
機能と考えることができます。( exec -a none script )
呼び出しに注意してくださいmain
(これはスクリプトにbash
渡されません。$0
このため-c
、最後のポイントで示したように使用する必要があります)。
したがって、いくつかのコーナーケースを除いてmain
、スクリプトが通常の方法で実行されたときにのみ呼び出されます。 通常、これは必要なものです。特に、理解しにくい複雑なコードが不足しているためです。
Pythonコードに非常に似ていることに注意してください。
if __name__ == '__main__': main()
main
スクリプトをインポート/ロードして強制することができるため、一部の例外を除いて、の呼び出しも防止されます。__name__='__main__'
複数のシェルからソースを取得できるものがある場合は、互換性がある必要があります。ただし(他の回答を読んで)、ING を検出するための(実装が簡単な)ポータブルな方法source
がないため、ルールを変更する必要があります。
スクリプトをで実行する必要があることを強制することにより/bin/bash
、正確にこれを実行します。
これはすべてのケースを解決しますが、以下の場合、スクリプトは直接実行できません。
/bin/bash
インストールされていないか、機能していない(つまり、ブート環境にある)curl https://example.com/script | $SHELL
bash
最近の場合にのみ当てはまります。このレシピは、特定のバリアントで失敗することが報告されています。そのため、自分のケースで機能することを確認してください。)ただし、それが必要な本当の理由や、まったく同じスクリプトを並行してソース化する機能については考えられません。通常、それをラップしてmain
手動で実行できます。そのように:
$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
この答えは、他のすべての答えの助けがなければ不可能でした!間違ったものでも-最初にこれを投稿させました。
更新:https ://stackoverflow.com/a/28776166/490291にある新しい発見により編集されました
/bin/sh
効果的であるbash
に割り当てる、POSIXモードでBASH_SOURCE
休憩スクリプト。他のシェルでは(dash
、ksh
、zsh
)、ファイルの引数として渡すことで、スクリプトを呼び出すシェル実行可能ファイルに直接誤動作(例えば、zsh <your-script>
スクリプトが誤ってそれがいると思うようになります調達)。(すでに、すべてのシェルでコードのパイプが誤動作していると述べました。)
. <your-script>
(ソーシング)は原則としてすべてのPOSIX風のシェルで機能しますが、スクリプトがPOSIX機能のみを使用するように明示的に記述されている場合にのみ意味があり、1つのシェルに固有の機能が実行を妨害しないようにします。他のシェルでは; したがって、(ではなく)Bashシバン行を使用すると#!/bin/sh
混乱が生じます-少なくとも目立つコメントはありません。逆に、スクリプトがBashのみから実行されることを意図している場合(たとえ移植できない機能を考慮しないためであっても)、非Bashシェルでの実行を拒否することをお勧めします。
main
が、この場合は実行されます。供給する場合と/bin/sh
、しているbash --posix
、同じことが、この場合に発生し、それは同様に平野間違っています。
BASH固有の答えを示します。コーンシェル、ごめんなさい。スクリプト名がであるとしますinclude2.sh
。次に、呼び出された内に関数を作成します。これが私のデモバージョンです:include2.sh
am_I_sourced
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
次に、さまざまな方法で実行してみます。
~/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
したがって、これは例外なく機能し、もろい$_
ものは使用していません。このトリックは、BASHのイントロスペクション機能、つまり組み込み変数FUNCNAME
とBASH_SOURCE
;を使用します。bashのマニュアルページにあるドキュメントを参照してください。
2つの警告のみ:
1)コールはするam_I_called
必要があります場所を取るでソース化スクリプトが、しない範囲内、任意の関数LEST ${FUNCNAME[1]}
何か他のものを返します。ええ...あなたはチェックすることができ${FUNCNAME[2]}
たでしょう-しかしあなたはただあなたの人生をより困難にします
2)含まれているファイルの名前を知りたい場合は、関数がソーススクリプトに存在するam_I_called
必要があります。
Dennisの非常に役立つ回答を少し修正して、少し移植性を高めたいと思います。
[ "$_" != "$0" ] && echo "Script is being sourced" || echo "Script is a subshell"
[[
(やや残忍な私見)Debian POSIX互換シェルによって認識されていないためですdash
。また、スペースを含むファイル名から保護するために引用符が必要になる場合もあります(このシェルでも同様です)。
$_
かなりもろいです。スクリプトで最初に行うこととして、それを確認する必要があります。それでも、シェルの名前(ソースの場合)またはスクリプトの名前(実行された場合)が含まれているとは限りません。
たとえば、ユーザーがを設定した場合、BASH_ENV
スクリプトの上部には、スクリプトで$_
最後に実行されたコマンドの名前が含まれBASH_ENV
ます。
私が見つけた最良の方法は、次の$0
ように使用することです:
name="myscript.sh"
main()
{
echo "Script was executed, running main..."
}
case "$0" in *$name)
main "$@"
;;
esac
残念ながら、functionargzero
オプションがその名前が示す以上のことを行い、デフォルトでオンになっているため、この方法はzshの初期状態では機能しません。
これを回避するために、を入れunsetopt functionargzero
ました.zshenv
。
それはすばらしいですが、kshの場合、次のように呼び出すと失敗する可能性があることに気付きました。
/bin/ksh -c ./myscript.sh
(それはそれが供給されていると考えていますが、それはサブシェルを実行するためではありません)しかし、式はこれを検出するように機能します:
/bin/ksh ./myscript.sh
また、式がコンパクトであっても、構文はすべてのシェルと互換性があるわけではありません。
したがって、bash、zsh、dash、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
エキゾチックなシェルのサポートを追加してください:)
ksh 93+u
、ksh ./myscript.sh
私にとっては問題なく動作します(私のステートメントを使用)-どのバージョンを使用していますか?
/proc/$$/cmdline
)を想定しておりdash
、(sh
たとえばUbuntu でも機能する)Linux のみに焦点を当てています。特定の仮定をする$0
用意がある場合は、移植可能な妥当な(ただし不完全な)テストを調べることができます。
sh
/ をサポートする移植性の最もよい近似であると私が考えるものに自由に適応できるようにしましたdash
。
[mac、linux]で動作するbash.version> = 3のワンライナーが必要でしたが、これらの答えはどれも適切ではありません。
[[ ${BASH_SOURCE[0]} = $0 ]] && main "$@"
bash
解決策は、(あなたが単純化できうまく動作します$BASH_SOURCE
)が、ksh
解決策は、堅牢ではありません:あなたのスクリプトはによって供給されている場合、別のスクリプト、あなたは偽陽性を得るでしょう。
要は、変数"$ 0"がシェルの名前と等しいかどうかを評価する必要があります。
このような:
#!/bin/bash
echo "First Parameter: $0"
echo
if [[ "$0" == "bash" ]] ; then
echo "The script was sourced."
else
echo "The script WAS NOT sourced."
fi
シェル経由:
$ bash check_source.sh
First Parameter: check_source.sh
The script WAS NOT sourced.
SOURCE経由:
$ source check_source.sh
First Parameter: bash
The script was sourced.
スクリプトがソースであるかどうかを検出するための100%ポータブルな方法を用意するのはかなり困難です。
私の経験(Shellscriptingで7年間)、唯一の安全な方法(PIDなどの環境変数に依存せず、それがVARIABLEであるという事実のため安全ではない)に関しては、次のことを行う必要があります。
どちらのオプションも自動スケーリングできませんが、より安全な方法です。
例えば:
SSHセッションを介してスクリプトをソースする場合、変数"$ 0"によって返される値(sourceを使用する場合)は-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
または
#!/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
/bin/bash -c '. ./check_source.sh'
できますThe script WAS NOT sourced.
。同じバグ:ln -s /bin/bash pumuckl; ./pumuckl -c '. ./check_source.sh'
->The script WAS NOT sourced.
私はチェックしてしまいました [[ $_ == "$(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
使用はときにcurl ... | bash -s -- ARGS
オンザフライでのリモートスクリプトを実行するには、$ 0がただになるbash
代わりに、通常の/bin/bash
私が使用して、実際のスクリプトファイルを実行したときにtype -p "$0"
はbashのフルパスを表示します。
テスト:
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
これは、「ユニバーサル」クロスシェルサポートに関する他のいくつかの回答からのスピンオフです。これは確かにhttps://stackoverflow.com/a/2942183/3220983と非常によく似ていますが、少し異なります。これの弱点は、クライアントスクリプトがその使用方法を尊重する必要があることです(つまり、最初に変数をエクスポートすることにより)。強みは、これが単純で「どこでも」機能することです。これがカットアンドペーストのテンプレートです。
# 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
注:export
このメカニズムをサブプロセスに拡張できることを確認して使用します。