我如何知道 Bash 脚本中的脚本文件名?
Posted
技术标签:
【中文标题】我如何知道 Bash 脚本中的脚本文件名?【英文标题】:How do I know the script file name in a Bash script? 【发布时间】:2010-09-16 13:53:55 【问题描述】:如何确定脚本本身内的 Bash 脚本文件的名称?
如果我的脚本在文件 runme.sh
中,那么我如何让它显示“您正在运行 runme.sh”消息而不进行硬编码?
【问题讨论】:
类似的[bash 脚本可以告诉它存储在哪个目录吗?](to ***.com/questions/59895/…) 目录见:Getting the source directory of a Bash script from within. 【参考方案1】:me=`basename "$0"`
要通过符号链接1 进行阅读,这通常不是您想要的(您通常不想以这种方式混淆用户),请尝试:
me="$(basename "$(test -L "$0" && readlink "$0" || echo "$0")")"
IMO,这会产生令人困惑的输出。 “我运行了 foo.sh,但它说我在运行 bar.sh!?一定是一个错误!”此外,使用不同名称的符号链接的目的之一是根据其名称提供不同的功能(想想某些平台上的 gzip 和 gunzip)。
1 也就是说,要解析符号链接,以便当用户执行 foo.sh
(实际上是指向 bar.sh
的符号链接)时,您希望使用解析后的名称 bar.sh
而不是 @987654327 @。
【讨论】:
$0 为您提供调用脚本的名称,而不是实际脚本文件的真实路径。 除非您通过符号链接被调用,否则它可以工作。但是,即便如此,它通常还是你想要的,IME。 不适用于源脚本而不是调用脚本。 @Charles Duffy:见Dimitre Radoulov's answer below。 -1, 1. readlink 只会深入一个符号链接,2. 第一个示例中的$0
会进行分词,3. $0
传递给 basename、readlink 和 echo处于允许将其视为命令行开关的位置。我建议改为me=$(basename -- "$0")
或更有效地牺牲可读性me=$0##*/
。对于符号链接,me=$(basename -- "$(readlink -f -- "$0")")
假设是 gnu utils,否则这将是一个很长的脚本,我不会在这里写。【参考方案2】:
echo "你正在运行 $0"
【讨论】:
不是 bash 脚本,是完整路径。【参考方案3】:您可以使用 $0 来确定您的脚本名称(带有完整路径) - 要获取脚本名称,您只能使用修剪该变量
basename $0
【讨论】:
【参考方案4】:如果脚本名称中有空格,更可靠的方法是使用 "$0"
或 "$(basename "$0")"
- 或在 MacOS 上:"$(basename \"$0\")"
。这可以防止名称以任何方式被破坏或解释。一般来说,在 shell 中总是用双引号括起变量名是个好习惯。
【讨论】:
+1 直接回答 + 简洁。如果需要符号链接功能,我建议您查看:Travis B. Hartwell's answer。【参考方案5】:要回答 Chris Conway,在 Linux 上(至少)你会这样做:
echo $(basename $(readlink -nf $0))
readlink 打印出符号链接的值。如果它不是符号链接,它会打印文件名。 -n 告诉它不打印换行符。 -f 告诉它完全跟随链接(如果符号链接是指向另一个链接的链接,它也会解析那个链接)。
【讨论】:
-n 是无害的,但不是必需的,因为 $(...) 结构会修剪它。【参考方案6】:如果你想要它没有路径,那么你会使用$0##*/
【讨论】:
如果我想要它没有任何可选的文件扩展名怎么办? 要删除扩展名,您可以尝试“$VARIABLE%.ext”,其中 VARIABLE 是您从 $0##*/ 获得的值,“.ext”是您的扩展名想要删除。【参考方案7】:这些答案对于他们陈述的情况是正确的,但是如果您使用“source”关键字从另一个脚本运行脚本(以便它在同一个 shell 中运行),仍然存在问题。在这种情况下,您将获得调用脚本的 $0。而且在这种情况下,我认为不可能获得脚本本身的名称。
这是一种极端情况,不应太认真。如果您直接从另一个脚本运行脚本(没有“源”),使用 $0 将起作用。
【讨论】:
你有一个很好的观点。不是边缘情况 IMO。不过有一个解决方案:见Dimitre Radoulov's answer above【参考方案8】:使用 bash >= 3 以下工作:
$ ./s
0 is: ./s
BASH_SOURCE is: ./s
$ . ./s
0 is: bash
BASH_SOURCE is: ./s
$ cat s
#!/bin/bash
printf '$0 is: %s\n$BASH_SOURCE is: %s\n' "$0" "$BASH_SOURCE"
【讨论】:
太棒了!这就是适用于 ./scrip.sh 和 source ./script.sh 的答案 这就是我想要的,而且很容易使用“dirname $BASE_SOURCE
”来获取脚本所在的目录。
在编写自删除脚本时,我差点学会了这种差异。幸运的是 'rm' 被别名为 'rm -i' :)
不管怎样。 ./s 获取名称 ./s 而不是 bash?我发现 $1 并不总是设置为 ./s...
它在 BASH_SOURCE 中,不是吗?【参考方案9】:
# - - - - - - - 脚本 - - - - - - - #
#!/bin/bash
echo
echo "# arguments called with ----> $@ "
echo "# \$1 ----------------------> $1 "
echo "# \$2 ----------------------> $2 "
echo "# path to me ---------------> $0 "
echo "# parent path --------------> $0%/* "
echo "# my name ------------------> $0##*/ "
echo
exit
# ------------- 调用 ------------- #
# 注意下一行,第一个参数在 double 中调用,
# 和单引号,因为它包含两个单词
$ /misc/shell_scripts/check_root/show_parms.sh "'你好'" "'william'"
# - - - - - - - 结果 - - - - - - - #
# 使用 ---> 'hello there' 'william' 调用的参数
# $1 ----------------------> '你好'
# $2 ----------------------> '威廉'
# 我的路径 --------------> /misc/shell_scripts/check_root/show_parms.sh
# 父路径 -------------> /misc/shell_scripts/check_root
# 我的名字 -----------------> show_parms.sh
# - - - - - - - 结尾 - - - - - - - #
【讨论】:
@NickC 见Substring Removal 我如何获得show_params
,即没有任何可选扩展名的名称?
如果从另一个文件夹调用脚本,则无法正常工作。该路径包含在$0##*/
中。使用 GitBash 测试。
上面的 .bash_login 脚本对我不起作用,但 $BASH_SOURCE 的 Dimitre Radoulov 解决方案效果很好。
@John 你的意思是 .bash_profile 吗?或者 .bashrc?您应该知道,它们的调用/获取方式存在一些细微的差异。我累死了,但如果我想对了,那很可能是问题所在。一个重要的地方是,如果您通过例如远程登录系统。 ssh 与本地系统。【参考方案10】:
回复:上面 Tanktalus 的(已接受)答案,稍微干净一点的方法是使用:
me=$(readlink --canonicalize --no-newline $0)
如果您的脚本来源于另一个 bash 脚本,您可以使用:
me=$(readlink --canonicalize --no-newline $BASH_SOURCE)
如果您的目标是向用户提供反馈,我同意取消引用符号链接会造成混淆,但有时您确实需要将规范名称获取到脚本或其他文件,这是最好的方法,海事组织。
【讨论】:
感谢source
d 脚本名称。【参考方案11】:
DIRECTORY=$(cd `dirname $0` && pwd)
我从另一个 Stack Overflow 问题 Can a Bash script tell what directory it's stored in? 中得到了上述信息,但我认为它对这个主题也很有用。
【讨论】:
【参考方案12】:$BASH_SOURCE
在获取脚本时给出了正确答案。
然而,这包括路径,因此仅获取脚本文件名,使用:
$(basename $BASH_SOURCE)
【讨论】:
这个答案是恕我直言最好的答案,因为解决方案使用了自记录代码。 $BASH_SOURCE 是完全可以理解的,无需阅读任何文档,而例如$0##*/ 不是 我相信这个答案更有价值,因为如果我们像. <filename> [arguments]
那样运行,$0
会给出调用者 shell 的名称。至少在 OSX 上是肯定的。
@AndreasM.Oberheim basename
的缺点是必须分叉一个单独的进程,而 ##*/
只是 bash 自己的处理。你仍然可以使用$BASH_SOURCE
,不过:$BASH_SOURCE[0]##*/
【参考方案13】:
this="$(dirname "$(realpath "$BASH_SOURCE")")"
这将解析符号链接(realpath 执行此操作),处理空格(双引号执行此操作),并且即使在源 (. ./myscript) 或由其他脚本调用($BASH_SOURCE 处理)时也会找到当前脚本名称。毕竟,最好将其保存在环境变量中以供重复使用或轻松复制到其他地方(this=)...
【讨论】:
仅供参考realpath
不是内置的 BASH 命令。它是一个独立的可执行文件,仅在某些发行版中可用
可以确认,在我的 14.04 框中它不可用,我不得不改用 readlink
【参考方案14】:
我发现无论文件是作为源文件还是作为脚本运行,此行始终有效。
echo "$BASH_SOURCE[$#BASH_SOURCE[@] - 1]"
如果您想跟踪符号链接,请在上面的路径上使用readlink
,递归或非递归。
使用BASH_SOURCE
环境变量及其关联的FUNCNAME
来解释单线工作的原因。
BASH_SOURCE
一个数组变量,其成员是源文件名,其中定义了 FUNCNAME 数组变量中的相应 shell 函数名称。 shell 函数 $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 手册]
【讨论】:
如果您从交互式会话中获取文件a
(假设a
的内容是echo "$BASH_SOURCE[$#BASH_SOURCE[@] - 1]"
),它会起作用-然后它将为您提供a
的路径。但是如果你写了一个脚本b
,里面有source a
并运行./b
,它会返回b
的路径。
你的答案不是给出第一个文件而不是最后/最新的文件吗?我发现 $BASH_SOURCE[0]" 或只是 $BASH_SOURCE 告诉我当前文件 - 即@Zainka 发布的答案。【参考方案15】:
感谢 Bill Hernandez 提供的信息。我添加了一些我正在采用的偏好。
#!/bin/bash
function Usage()
echo " Usage: show_parameters [ arg1 ][ arg2 ]"
[[ $#2 -eq 0 ]] && Usage ||
echo
echo "# arguments called with ----> $@ "
echo "# \$1 -----------------------> $1 "
echo "# \$2 -----------------------> $2 "
echo "# path to me ---------------> $0 " | sed "s/$USER/\$USER/g"
echo "# parent path --------------> $0%/* " | sed "s/$USER/\$USER/g"
echo "# my name ------------------> $0##*/ "
echo
干杯
【讨论】:
【参考方案16】:像这样?
export LC_ALL=en_US.UTF-8
#!/bin/bash
#!/bin/sh
#----------------------------------------------------------------------
start_trash()
ver="htrash.sh v0.0.4"
$TRASH_DIR # url to trash $MY_USER
$TRASH_SIZE # Show Trash Folder Size
echo "Would you like to empty Trash [y/n]?"
read ans
if [ $ans = y -o $ans = Y -o $ans = yes -o $ans = Yes -o $ans = YES ]
then
echo "'yes'"
cd $TRASH_DIR && $EMPTY_TRASH
fi
if [ $ans = n -o $ans = N -o $ans = no -o $ans = No -o $ans = NO ]
then
echo "'no'"
fi
return $TRUE
#-----------------------------------------------------------------------
start_help()
echo "HELP COMMANDS-----------------------------"
echo "htest www open a homepage "
echo "htest trash empty trash "
return $TRUE
#end Help
#-----------------------------------------------#
homepage=""
return $TRUE
#end cpdebtemp
# -Case start
# if no command line arg given
# set val to Unknown
if [ -z $1 ]
then
val="*** Unknown ***"
elif [ -n $1 ]
then
# otherwise make first arg as val
val=$1
fi
# use case statement to make decision for rental
case $val in
"trash") start_trash ;;
"help") start_help ;;
"www") firefox $homepage ;;
*) echo "Sorry, I can not get a $val for you!";;
esac
# Case stop
【讨论】:
-1,不回答问题(不显示如何找到脚本的名称),并且在一个非常错误的脚本中是一个令人困惑的示例。【参考方案17】:如果你调用 shell 脚本像
/home/mike/runme.sh
$0 是全名
/home/mike/runme.sh
basename $0 将获取基本文件名
runme.sh
你需要把这个基本名称放到一个变量中
filename=$(basename $0)
并添加您的附加文本
echo "You are running $filename"
所以你的脚本就像
/home/mike/runme.sh
#!/bin/bash
filename=$(basename $0)
echo "You are running $filename"
【讨论】:
【参考方案18】:echo "$(basename "`test -L $BASH_SOURCE[0] \
&& readlink $BASH_SOURCE[0] \
|| echo $BASH_SOURCE[0]`")"
【讨论】:
【参考方案19】:在bash
中,您可以使用$0
获取脚本文件名。通常$1
、$2
等用于访问 CLI 参数。同样$0
是访问触发脚本的名称(脚本文件名)。
#!/bin/bash
echo "You are running $0"
...
...
如果您使用/path/to/script.sh
之类的路径调用脚本,那么$0
也会给出带有路径的文件名。在这种情况下需要使用$(basename $0)
来获取脚本文件名。
【讨论】:
【参考方案20】:由于一些 cmets 询问不带扩展名的文件名,下面是一个示例:
FileName=$0##*/
FileNameWithoutExtension=$FileName%.*
享受吧!
【讨论】:
这是黑魔法!【参考方案21】:这是我的想法,灵感来自Dimitre Radoulov 的回答
script="$BASH_SOURCE"
[ -z "$BASH_SOURCE" ] && script="$0"
echo "Called $script with $# argument(s)"
不管你如何调用你的脚本
. path/to/script.sh
或
./path/to/script.sh
【讨论】:
【参考方案22】:简洁明了,在my_script.sh
#!/bin/bash
running_file_name=$(basename "$0")
echo "You are running '$running_file_name' file."
输出:
./my_script.sh
You are running 'my_script.sh' file.
【讨论】:
【参考方案23】:这适用于./self.sh
、~/self.sh
、source self.sh
、source ~/self.sh
:
#!/usr/bin/env bash
self=$(readlink -f "$BASH_SOURCE[0]")
basename=$(basename "$self")
echo "$self"
echo "$basename"
致谢:我结合了多个答案来得到这个。
【讨论】:
$(readlink -f "$BASH_SOURCE[0]")
仅在您使用 bash 时有效。 $(readlink -f "$BASH_SOURCE:-$0")
无论如何都可以使用。【参考方案24】:
$0 将给出您正在运行的脚本的名称。创建一个脚本文件并添加以下代码
#!/bin/bash
echo "Name of the file is $0"
然后像这样从终端运行
./file_name.sh
【讨论】:
以上是关于我如何知道 Bash 脚本中的脚本文件名?的主要内容,如果未能解决你的问题,请参考以下文章
Bash脚本创建10个文件夹,文本文件包含每个文件中的当前日期