sourcedシェルスクリプトが自身へのパスを見つける方法はありますか?私は主にbashに関心がありますが、tcshを使用する同僚が何人かいます。
ソーシングによって現在のシェルでコマンドが実行されるため、ここではそれほどの運がないと思います。したがって、$0
は、現在のシェルの呼び出しであり、ソーススクリプトではありません。現在、私の最善の考えは、source $script $script
、最初の定位置パラメーターに必要な情報が含まれるようにします。誰かがより良い方法を持っていますか?
明確にするために、私はsourcingスクリプトであり、実行していません。
source foo.bash
tcsh
では、スクリプトの先頭の$_
には、ファイルが読み込まれた場合はその場所が含まれ、実行された場合は$0
が含まれます。
#!/bin/tcsh
set sourced=($_)
if ("$sourced" != "") then
echo "sourced $sourced[2]"
endif
if ("$0" != "tcsh") then
echo "run $0"
endif
バッシュで:
#!/bin/bash
[[ $0 != $BASH_SOURCE ]] && echo "Script is being sourced" || echo "Script is being run"
$BASH_SOURCE
変数を使用できると思います。実行されたパスを返します。
pbm@tauri ~ $ /home/pbm/a.sh
/home/pbm/a.sh
pbm@tauri ~ $ ./a.sh
./a.sh
pbm@tauri ~ $ source /home/pbm/a.sh
/home/pbm/a.sh
pbm@tauri ~ $ source ./a.sh
./a.sh
したがって、次のステップでは、パスが相対パスかどうかを確認する必要があります。相対的でない場合は、すべてがOKです。パスがpwd
で確認できる場合は、/
および$BASH_SOURCE
と連結します。
このソリューションはbashにのみ適用され、tcshには適用されません。関数内からパスを見つけようとすると、一般的に提供される回答${BASH_SOURCE[0]}
が機能しないことに注意してください。
ファイルがソースであるかスクリプトとして実行されているかに関係なく、この行は常に機能することがわかりました。
echo ${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}
シンボリックリンクを追跡したい場合は、上に到達したパスでreadlink
を再帰的または非再帰的に使用します。
これを試して、他の提案されたソリューションと比較するためのスクリプトを次に示します。 source test1/test2/test_script.sh
またはbash test1/test2/test_script.sh
として呼び出します。
#
# Location: test1/test2/test_script.sh
#
echo $0
echo $_
echo ${BASH_SOURCE}
echo ${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}
cur_file="${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}"
cur_dir="$(dirname "${cur_file}")"
source "${cur_dir}/func_def.sh"
function test_within_func_inside {
echo ${BASH_SOURCE}
echo ${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}
}
echo "Testing within function inside"
test_within_func_inside
echo "Testing within function outside"
test_within_func_outside
#
# Location: test1/test2/func_def.sh
#
function test_within_func_outside {
echo ${BASH_SOURCE}
echo ${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}
}
ワンライナーが機能する理由は、BASH_SOURCE
環境変数とそれに関連するFUNCNAME
の使用によって説明されます。
BASH_SOURCE
FUNCNAME配列変数内の対応するシェル関数名が定義されているソースファイル名をメンバーとする配列変数。シェル関数$ {FUNCNAME [$ i]}はファイル$ {BASH_SOURCE [$ i]}で定義され、$ {BASH_SOURCE [$ i + 1]}から呼び出されます。
FUNCNAME
現在実行呼び出しスタックにあるすべてのシェル関数の名前を含む配列変数。インデックス0の要素は、現在実行中のシェル関数の名前です。一番下の要素(最高のインデックスを持つ要素)は「メイン」です。この変数は、シェル関数の実行中にのみ存在します。 FUNCNAMEへの割り当ては効果がなく、エラーステータスを返します。 FUNCNAMEが設定されていない場合、その後リセットされても、特殊なプロパティは失われます。
この変数は、BASH_LINENOおよびBASH_SOURCEで使用できます。 FUNCNAMEの各要素には、BASH_LINENOおよびBASH_SOURCEに対応する要素があり、呼び出しスタックを記述します。たとえば、$ {FUNCNAME [$ i]}はファイル$ {BASH_SOURCE [$ i + 1]}から行番号$ {BASH_LINENO [$ i]}で呼び出されました。組み込みの呼び出し元は、この情報を使用して現在の呼び出しスタックを表示します。
[出典:バッシュマニュアル]
完全性とサーチャーのために、ここでこれらの機能を説明します...これはコミュニティーWikiなので、他のシェルの同等のものを自由に追加してください(明らかに、$ BASH_SOURCEは異なります)。
test.sh:
#! /bin/sh
called=$_
echo $called
echo $_
echo $0
echo $BASH_SOURCE
test2.sh:
#! /bin/sh
source ./test.sh
$./test2.sh
./test2.sh
./test2.sh
./test2.sh
./test.sh
$ sh ./test2.sh
/bin/sh
/bin/sh
./test2.sh
./test.sh
$./test2.sh
./test2.sh
./test2.sh
./test2.sh
$/bin/sh ./test2.sh
/bin/sh
/bin/sh
./test2.sh
$
$ ./test2.sh
./test.sh
./test.sh
./test.sh
$ zsh test.sh
echo
test.sh
$
これはbash、dash、ksh、zshで私にとってはうまくいきました:
if test -n "$BASH" ; then script=$BASH_SOURCE
Elif test -n "$TMOUT"; then script=${.sh.file}
Elif test -n "$ZSH_NAME" ; then script=${(%):-%x}
Elif test ${0##*/} = dash; then x=$(lsof -p $$ -Fn0 | tail -1); script=${x#n}
else script=$0
fi
echo $script
これらのシェルの出力:
BASH source: ./myscript
ZSH source: ./myscript
KSH source: /home/pbrannan/git/theme/src/theme/web/myscript
DASH source: /home/pbrannan/git/theme/src/theme/web/myscript
BASH: ./myscript
ZSH: ./myscript
KSH: /home/pbrannan/git/theme/src/theme/web/myscript
DASH: ./myscript
私はそれをcsh/tcshで動作させようとしましたが、それは難しいです。私はPOSIXに固執しています。
コミュニティのwikiの回答(Shawn J. Goffからの)に少し混乱したので、整理するためのスクリプトを書きました。 $_
について、これを見つけました: _
の使用法、コマンドに渡される環境変数として 。これは環境変数なので、値を誤ってテストするのは簡単です。
以下はスクリプトで、出力されます。それらは this Gist にもあります。
#!/bin/bash
# test-Shell-default-variables.sh
# Usage examples (you might want to `Sudo apt install zsh ksh`):
#
# ./test-Shell-default-variables.sh dash bash
# ./test-Shell-default-variables.sh dash bash zsh ksh
# ./test-Shell-default-variables.sh dash bash zsh ksh | less -R
# `-R` in `less -R` to have less pass escape sequences directly to the terminal
# so we have colors.
# The "invoking with name `sh`" tests are commented because for every Shell I
# tested (dash, bash, zsh and ksh), the output was the same as that of dash.
# The `test_expression` function also work with expansion changes. You can try
# lines like `test_expression '{BASH_SOURCE:-$0}'`.
echolor() {
echo -e "\e[1;36m$@\e[0m"
}
tell_file() {
echo File \`"$1"\` is:
echo \`\`\`
cat "$1"
echo \`\`\`
echo
}
Shell_ARRAY=("$@")
test_command() {
for Shell in "${Shell_ARRAY[@]}"
do
prepare "$Shell"
cmd="$(eval echo $1)"
# echo "cmd: $cmd"
printf '%-4s: ' "$Shell"
{ env -i $cmd 2>&1 1>&3 | sed 's/^/[err]/'; } 3>&1
teardown
done
echo
}
prepare () {
Shell="$1"
PATH="$PWD/$Shell/sh:$PATH"
}
teardown() {
PATH="${PATH#*:}"
}
###
### prepare
###
for Shell in "${Shell_ARRAY[@]}"
do
mkdir "$Shell"
ln -sT "/bin/$Shell" "$Shell/sh"
done
echo > printer.sh
echo '. ./printer.sh' > sourcer.sh
rm linked.sh &>/dev/null; ln -sT "printer.sh" "linked.sh"
tell_file sourcer.sh
###
### run
###
test_expression() {
local expr="$1"
# prepare
echo "echo $expr" > printer.sh
tell_file printer.sh
# run
cmd='$Shell ./printer.sh'
echolor "\`$cmd\` (simple invocation) ($expr):"
test_command "$cmd"
# cmd='sh ./printer.sh'
# echolor "\`$cmd\` (when executable name is \`sh\`) ($expr):"
# test_command "$cmd"
cmd='$Shell ./sourcer.sh'
echolor "\`$cmd\` (via sourcing) ($expr):"
test_command "$cmd"
# cmd='sh ./sourcer.sh'
# echolor "\`$cmd\` (via sourcing, when name is \`sh\`) ($expr):"
# test_command "$cmd"
cmd='$Shell ./linked.sh'
echolor "\`$cmd\` (via symlink) ($expr):"
test_command "$cmd"
# cmd='sh ./linked.sh'
# echolor "\`$cmd\` (via symlink, when name is \`sh\`) ($expr):"
# test_command "$cmd"
echolor "------------------------------------------"
echo
}
test_expression '$BASH_SOURCE'
test_expression '$0'
test_expression '$(/bin/true x y; true a b c; echo $_)' # Rq: true is a builtin
test_expression '$_'
###
### teardown
###
for Shell in "${Shell_ARRAY[@]}"
do
rm "$Shell/sh"
rm -d "$Shell"
done
rm sourcer.sh
rm linked.sh
rm printer.sh
./test-Shell-default-variables.sh {da,ba,z,k}sh
の出力File `sourcer.sh` is:
```
. ./printer.sh
```
File `printer.sh` is:
```
echo $BASH_SOURCE
```
`$Shell ./printer.sh` (simple invocation) ($BASH_SOURCE):
dash:
bash: ./printer.sh
zsh :
ksh :
`$Shell ./sourcer.sh` (via sourcing) ($BASH_SOURCE):
dash:
bash: ./printer.sh
zsh :
ksh :
`$Shell ./linked.sh` (via symlink) ($BASH_SOURCE):
dash:
bash: ./linked.sh
zsh :
ksh :
------------------------------------------
File `printer.sh` is:
```
echo $0
```
`$Shell ./printer.sh` (simple invocation) ($0):
dash: ./printer.sh
bash: ./printer.sh
zsh : ./printer.sh
ksh : ./printer.sh
`$Shell ./sourcer.sh` (via sourcing) ($0):
dash: ./sourcer.sh
bash: ./sourcer.sh
zsh : ./printer.sh
ksh : ./sourcer.sh
`$Shell ./linked.sh` (via symlink) ($0):
dash: ./linked.sh
bash: ./linked.sh
zsh : ./linked.sh
ksh : ./linked.sh
------------------------------------------
File `printer.sh` is:
```
echo $(/bin/true x y; true a b c; echo $_)
```
`$Shell ./printer.sh` (simple invocation) ($(/bin/true x y; true a b c; echo $_)):
dash:
bash: c
zsh : c
ksh :
`$Shell ./sourcer.sh` (via sourcing) ($(/bin/true x y; true a b c; echo $_)):
dash:
bash: c
zsh : c
ksh :
`$Shell ./linked.sh` (via symlink) ($(/bin/true x y; true a b c; echo $_)):
dash:
bash: c
zsh : c
ksh :
------------------------------------------
File `printer.sh` is:
```
echo $_
```
`$Shell ./printer.sh` (simple invocation) ($_):
dash:
bash: bash
zsh :
ksh :
`$Shell ./sourcer.sh` (via sourcing) ($_):
dash:
bash: bash
zsh : ./printer.sh
ksh :
`$Shell ./linked.sh` (via symlink) ($_):
dash:
bash: bash
zsh :
ksh :
------------------------------------------
$BASH_SOURCE
$BASH_SOURCE
は、bashでのみ機能します。$0
との唯一の違いは、現在のファイルのソースが別のファイルである場合です。その場合、$BASH_PROFILE
には、サワーファイルの名前ではなく、ソースファイルの名前が含まれます。$0
$0
の値はbashの$BASH_SOURCE
と同じです。$_
$_
は、ダッシュとkshの影響を受けません。$_
は最後の呼び出しの最後の引数まで減衰します。$_
を「bash」に初期化します。$_
をそのままにします。 (ソーシング時は、「最後の引数」ルールの結果のみです)。sh
という名前のシンボリックリンクを介してbashまたはzshが呼び出されると、それらのテストに関しては、ダッシュのように動作します。この答え は、lsof
と少しのgrepマジックが、tcshでネストされたソースファイルを処理する可能性があると思われる唯一の方法を説明しています。
/usr/sbin/lsof +p $$ | grep -oE /.\*source_me.tcsh
tl; drscript=$(readlink -e -- "${BASH_SOURCE}")
(forbash明らかに)
$BASH_SOURCE
テストケース与えられたファイル/tmp/source1.sh
echo '$BASH_SOURCE '"(${BASH_SOURCE})"
echo 'readlink -e $BASH_SOURCE'\
"($(readlink -e -- "${BASH_SOURCE}"))"
source
さまざまな方法でファイル
source
from /tmp
$> cd /tmp
$> source source1.sh
$BASH_SOURCE (source1.sh)
readlink -e $BASH_SOURCE (/tmp/source1.sh)
$> source ./source1.sh
$BASH_SOURCE (./source1.sh)
readlink -e $BASH_SOURCE (/tmp/source1.sh)
$> source /tmp/source1.sh
$BASH_SOURCE (/tmp/source1.sh)
readlink -e $BASH_SOURCE (/tmp/source1.sh)
source
from /
cd /
$> source /tmp/source1.sh
$0 (bash)
$BASH_SOURCE (/tmp/source1.sh)
readlink -e $BASH_SOURCE (/tmp/source1.sh)
source
異なる相対パスから/tmp/a
および/var
$> cd /tmp/a
$> source ../source1.sh
$BASH_SOURCE (../source1.sh)
readlink -e $BASH_SOURCE (/tmp/source1.sh)
$> cd /var
$> source ../tmp/source1.sh
$BASH_SOURCE (../tmp/source1.sh)
readlink -e $BASH_SOURCE (/tmp/source1.sh)
$0
すべての場合で、スクリプトにコマンドが追加されている場合
echo '$0 '"(${0})"
次にsource
常に出力されるスクリプト
$0 (bash)
ただし、スクリプトがrunの場合、たとえば.
$> bash /tmp/source1.sh
次に$0
は文字列値/tmp/source1.sh
。
$0 (/tmp/source1.sh)
$BASH_SOURCE (/tmp/source1.sh)
readlink -e $BASH_SOURCE (/tmp/source1.sh)
Bashシェルの場合、 @ Dennis Williamsonの回答 が最も役に立ちましたが、Sudo
の場合は機能しませんでした。これは:
if ( [[ $_ != $0 ]] && [[ $_ != $Shell ]] ); then
echo "I'm being sourced!"
exit 1
fi
Ifステートメントを使用する代わりに、スクリプトをbash互換とzsh互換の両方にするには、単に${BASH_SOURCE[0]:-${(%):-%x}}
と記述します。結果の値は、定義されている場合は_BASH_SOURCE[0]
_から取得され、BASH_SOURCE [0]が定義されていない場合は${(%):-%x}}
から取得されます。
最もトリッキーな部分は、Ubuntuでsh置換として使用されているダッシュシェル用の現在のソースファイルを見つけることです。次のコードスニペットをソースとして使用するスクリプトで使用して、その絶対パスを決定できます。 bash、zsh、およびdashでテストされ、dashおよびshの両方として呼び出されます。
注意:現代に依存 realpath(1) ユーティリティGNU coreutilsパッケージ
注意:lsof(1)オプションも確認する必要があります。これは、Ubuntu 18および19では、このページと他のページの両方から同様のアドバイスが機能しなかったため、これを再発明する必要があったためです。
getShellName() {
[ -n "$BASH" ] && echo ${BASH##/*/} && return
[ -n "$ZSH_NAME" ] && echo $ZSH_NAME && return
echo ${0##/*/}
}
getCurrentScript() {
local result
case "$(getShellName)" in
bash ) result=${BASH_SOURCE[0]}
;;
zsh ) emulate -L zsh
result=${funcfiletrace[1]%:*}
;;
dash | sh )
result=$(
lsof -p $$ -Fn \
| tail --lines=1 \
| xargs --max-args=2 \
| cut --delimiter=' ' --fields=2
)
result=${result#n}
;;
* ) result=$0
;;
esac
echo $(realpath $result)
}