Answers:
在中tcsh
,$_
脚本的开头将包含脚本的位置(如果文件是源文件)和$0
包含文件(如果文件已运行)。
#!/bin/tcsh
set sourced=($_)
if ("$sourced" != "") then
echo "sourced $sourced[2]"
endif
if ("$0" != "tcsh") then
echo "run $0"
endif
在Bash中:
#!/bin/bash
[[ $0 != $BASH_SOURCE ]] && echo "Script is being sourced" || echo "Script is being run"
> tcsh --version\n tcsh 6.14.00 (Astron) 2005-03-25 (i486-intel-linux) options wide,nls,dl,al,kan,rh,nd,color,filec
。至于非交互地采购它,就像您在原始问题中提到的那样,源文件被包括在父文件中,就好像它实际上是它的一部分一样。我认为您的位置参数解决方法可能是最好的方法。但是,通常的问题是“你为什么要这么做”和往常一样回答该回复是“不这样做-做这个,而不是”在哪里“这个”往往是存储...
.
,并source
在这方面同样奏效。请注意,$_
必须在文件的第一条语句中访问它,否则它将包含上一条命令的最后一个参数。我喜欢将shebang包括在内以作为我自己的参考,因此我知道它应该用于什么外壳以及用于编辑器,因此它使用语法突出显示。
source
,然后做测试.
。抱歉,我不称职。它们确实是相同的。无论如何,$BASH_SOURCE
作品。
我认为您可以使用$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
因此,在下一步中,我们应该检查路径是否相对。如果不是相对的,那么一切都很好。如果是的话,我们可以用检查路径pwd
,用/
和连接$BASH_SOURCE
。
source
,$PATH
则进行搜索/
。搜索顺序取决于外壳选项,有关详细信息,请参见手册。
mydir="$(cd "$(dirname "$BASH_SOURCE")"; pwd)"
会起作用吗?
此解决方案仅适用于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]}中调用。
功能名称
一个数组变量,包含当前在执行调用堆栈中的所有shell函数的名称。索引为0的元素是任何当前正在执行的Shell函数的名称。最底部的元素(具有最高索引的元素)是“ main”。仅当执行Shell函数时,此变量才存在。分配给FUNCNAME无效,并返回错误状态。如果未设置FUNCNAME,则即使随后将其重置,它也会失去其特殊属性。
此变量可以与BASH_LINENO和BASH_SOURCE一起使用。FUNCNAME的每个元素在BASH_LINENO和BASH_SOURCE中都有相应的元素来描述调用堆栈。例如,从文件$ {BASH_SOURCE [$ i + 1]}在行号$ {BASH_LINENO [$ i]}处调用了$ {FUNCNAME [$ i]}。内置呼叫方使用此信息显示当前呼叫堆栈。
[来源:Bash手册]
为了彻底和方便搜索者,这是它们的工作...这是一个社区Wiki,因此可以随时添加其他Shell的等效项(显然,$ 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
$
called=$_; echo $called; echo $_
?此打印不会$_
两次吗?
$_
特殊参数:“在shell启动时,设置为用于调用在环境或参数列表中传递的正在执行的shell或shell脚本的绝对路径名。随后,扩展到最后一个扩展后,前一个命令的参数。还要设置为用于调用执行的每个命令的完整路径名,并放在导出到该命令的环境中。检查邮件时,此参数保存邮件文件的名称。”
#! /bin/sh
,使其无法使用。这将启动/bin/sh
,设置变量的新实例,然后退出该实例,使调用实例保持不变。
#
以Shell脚本开头的内容都是注释。 #!
(shebang)仅在执行脚本的第一行时才具有特殊含义。 作为源文件的第一行,这只是一条注释。
这在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)有点困惑,所以我写了一个脚本来整理问题。关于$_
,我发现了这一点:将_
用作环境变量并传递给command。这是一个环境变量,因此很容易错误地测试其值。
下面是脚本,然后输出。他们也在这一要旨中。
#!/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_SOURCE
bash中相同的值。$_
$_
破折号和ksh保持不变。$_
衰减到最后一次调用的最后一个参数。$_
为“ bash”。$_
不变。(采购时,它只是“最后一个论点”规则的结果)。sh
,对于这些测试,其行为类似于破折号。对于bash shell,我发现@Dennis Williamson的答案最有帮助,但在的情况下不起作用sudo
。这样做:
if ( [[ $_ != $0 ]] && [[ $_ != $SHELL ]] ); then
echo "I'm being sourced!"
exit 1
fi
tl; dr script=$(readlink -e -- "${BASH_SOURCE}")
(显然是 bash)
$BASH_SOURCE
测试用例给定文件 /tmp/source1.sh
echo '$BASH_SOURCE '"(${BASH_SOURCE})"
echo 'readlink -e $BASH_SOURCE'\
"($(readlink -e -- "${BASH_SOURCE}"))"
source
该文件以不同的方式
source
从 /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
从 /
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)
但是,如果脚本已运行,例如
$> bash /tmp/source1.sh
那么$0
将是字符串值/tmp/source1.sh
。
$0 (/tmp/source1.sh)
$BASH_SOURCE (/tmp/source1.sh)
readlink -e $BASH_SOURCE (/tmp/source1.sh)
wdir="$PWD"; [ "$PWD" = "/" ] && wdir=""
case "$0" in
/*) scriptdir="${0%/*}";;
*) scriptdir="$wdir/${0#./}"; scriptdir="${scriptdir%/*}";;
esac
echo "$scriptdir"
也许这不适用于符号链接或源文件,但适用于普通文件。来回参考。@kenorb没有目录名,readlink,BASH_SOURCE。
$0
您获得有关当前正在运行的脚本而不是源脚本的信息。
实际上,“ dirname $ 0”将为您提供脚本的路径,但是您必须对其进行一些解释:
$ cat bash0
#!/bin/bash
echo \$0=$0
dirname $0
$ bash0 # "." appears in PATH right now.
$0=./bash0
.
$ ./bash0
$0=./bash0
.
$ $PWD/bash0
$0=/home/00/bediger/src/ksh/bash0
/home/00/bediger/src/ksh
$ $PWD/../ksh/bash0
$0=/home/00/bediger/src/ksh/../ksh/bash0
/home/00/bediger/src/ksh/../ksh
$ ../ksh/bash0
$0=../ksh/bash0
../ksh
您必须准备处理“。” 在某些常见情况下用作目录名称。我会做一些实验,因为我记得当“。”时ksh内置的目录名有所不同。出现在PATH中。
$0
仅包含用于交互式shell的“ bash”,这就是所有源脚本所看到的。