我如何知道Bash脚本中的脚本文件名?


605

如何确定脚本本身内部的Bash脚本文件的名称?

就像我的脚本在文件中一样runme.sh,那么我如何在不进行硬编码的情况下显示“您正在运行runme.sh”消息呢?


3
类似[可bash脚本告诉一下目录及其存储在?(至stackoverflow.com/questions/59895/...
罗德里格-

有关目录,请参见:从中获取Bash脚本的源目录
kenorb

Answers:


630
me=`basename "$0"`

要通读通常不是您想要的符号链接1(您通常不希望这样使用户感到困惑),请尝试:

me="$(basename "$(test -L "$0" && readlink "$0" || echo "$0")")"

海事组织,这将产生令人困惑的输出。“我运行了foo.sh,但这是说我正在运行bar.sh !?一定是bug!” 此外,具有不同名称的符号链接的目的之一是根据它的名称提供不同的功能(在某些平台上可以考虑一下gzip和gunzip)。


1也就是说,要解析符号链接,以便在用户执行时foo.sh实际上是指向的符号链接bar.sh,您希望使用解析的名称bar.sh而不是foo.sh


40
$ 0为您提供调用脚本的名称,而不是实际脚本文件的真实路径。
克里斯·康威

4
除非通过symlink调用您,否则它会起作用。但是,即使那样,IME通常还是您想要的。
Tanktalus

78
不适用于源代码而非调用的脚本。
查尔斯·达菲,

1
@查尔斯·达菲(Charles Duffy):请参阅下面的Dimitre Radoulov的回答
Serge Wautier,2011年

8
-1,1. readlink将仅行进一个symlink的深度,2. $0在第一个示例中,将进行单词拆分,3. $0将其传递给基本名称,readlink和echo的位置应允许将其视为命令行开关。我建议改为me=$(basename -- "$0")以牺牲可读性为代价,或者更有效地降低可读性me=${0##*/}。对于符号链接,me=$(basename -- "$(readlink -f -- "$0")")假设使用gnu utils,否则它将是一个很长的脚本,在这里我不会写。
2015年

246
#-------------脚本------------- ##-------------已调用------ -------#

#!/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



#注意下一行,第一个参数在双引号,#和单引号中被调用,因为它包含两个单词


$   / misc / shell_scripts / check_root / show_parms sh “'你好,那里”“ ”“威廉” 

#-------------结果-------------#

#用--->'hello there''william' #$ 1 调用的参数---------------------->'hello there' #$ 2 ---- ------------------>'william' #我的路径--------------> / misc / shell_scripts / check_root / show_parms。 sh #父路径-------------> / misc / shell_scripts / check_root #我的名字-----------------> show_parms.sh






#  -  -  -  -  -  - - 结束  -  -  -  -  -  - - #

11

1
我如何获得just show_params,即没有任何可选扩展名的名称?
Acumenus

如果脚本是从另一个文件夹中调用的,则无法使用。该路径包含在中${0##*/}。使用GitBash测试。
AlikElzin-kilaka

1
上面的代码不适用于.bash_login脚本,但是$ BASH_SOURCE的Dimitre Radoulov解决方案非常有效。
约翰

@John当然是.bash_profile吗?或.bashrc吗?您应该知道,在调用/来源方式上有一些细微的差异。我很累,但是如果我想对了,那很可能是问题所在。一个重要的地方是您是否要通过ssh而非本地系统远程登录系统。
Pryftan

193

使用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"

24
大!这就是适用于./scrip.sh和源./script.sh的答案
zhaorufei 2010年

4
这就是我想要的,并且很容易使用“ dirname $BASE_SOURCE”来获取脚本所在的目录。
拉里·蔡

2
在编写自删除脚本时,我几乎很难理解到这种差异。幸运的是'rm'被别名为'rm -i':)
kervin 2012年

无论如何。./s以获得名称./s而不是bash?我发现$ 1并不总是设置为./s ...
David Mokon Bond

1
BASH_SOURCE中,对吗?
Dimitre Radoulov

69

$BASH_SOURCE 在采购脚本时给出正确的答案。

但是,这包括路径,因此仅获取脚本文件名,请使用:

$(basename $BASH_SOURCE) 

2
恕我直言,这个答案是最好的答案,因为该解决方案使用了自记录代码。$ BASH_SOURCE完全可以理解,无需阅读任何文档,而例如$ {0 ## * /}则不是
Andreas M. Oberheim

我认为这个答案更具价值,因为如果我们像这样运行. <filename> [arguments]$0将给出调用方外壳的名称。至少可以肯定在OSX上。
Mihir

68

如果脚本名称中有空格,一个更强大的方法是使用"$0""$(basename "$0")"-或在MacOS: "$(basename \"$0\")"。这样可以防止以任何方式混淆或解释该名称。通常,良好的做法是始终在外壳程序中双引号。


2
+1直接回答+简洁。如果需要符号链接功能,建议您查看:Travis B. Hartwell的答案。
Trevor Boyd Smith

26

如果您想要它没有路径,那么您将使用 ${0##*/}


如果我要它没有任何可选的文件扩展名怎么办?
Acumenus

要删除扩展名,您可以尝试“ $ {VARIABLE%.ext}”,其中VARIABLE是从$ {0 ## * /}获得的值,而“ .ext”是要删除的扩展名。
BrianV

19

要回答Chris Conway,至少在Linux上,您可以这样做:

echo $(basename $(readlink -nf $0))

readlink打印出符号链接的值。如果不是符号链接,则打印文件名。-n告诉它不打印换行符。-f告诉它完全跟随该链接(如果符号链接是到另一个链接的链接,则也会解析该链接)。


2
-n无害,但不是必需的,因为$(...)结构将对其进行修剪。
zhaorufei 2010年

16

我发现此行始终有效,无论文件是源文件还是作为脚本运行。

echo "${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}"

如果要跟随符号链接readlink在递归或非递归的路径上使用。

使用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手册]


2
如果您从交互式会话中获取文件a (假定a内容为echo "${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}"),那么它将起作用-然后它将为您a提供路径。但是,如果你写一个脚本bsource a它和运行./b,它会返回b的路径。
PSkocik 2015年

12

这些答案对于所陈述的情况是正确的,但是如果您使用'source'关键字从另一个脚本运行该脚本(以使其在同一外壳中运行),则仍然存在问题。在这种情况下,您将获得调用脚本的$ 0。在这种情况下,我认为不可能获得脚本本身的名称。

这是一个极端的情况,不应太当真。如果直接从另一个脚本(不带“源”)运行该脚本,则可以使用$ 0。


2
您有一个很好的观点。海事组织不是一个极端情况。不过,有一个解决方案:请参阅上方Dimitre Radoulov的回答
Serge Wautier

10

由于一些评论询问了不带扩展名的文件名,因此以下是一个示例,该示例说明了如何实现:

FileName=${0##*/}
FileNameWithoutExtension=${FileName%.*}

请享用!


9

回复:上面的Tanktalus(已接受)答案,一种更简洁的方法是使用:

me=$(readlink --canonicalize --no-newline $0)

如果您的脚本来自另一个bash脚本,则可以使用:

me=$(readlink --canonicalize --no-newline $BASH_SOURCE)

我同意,如果您的目标是向用户提供反馈,则取消引用符号链接会令人困惑,但是有时您确实需要将规范名称获取到脚本或其他文件,这是imo的最佳方法。


1
感谢您的sourced脚本名称。
Fredrick Gauss 2015年

8

您可以使用$ 0来确定脚本名称(具有完整路径)-仅获取脚本名称,您可以使用

basename $0

8

如果您的调用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"

7
this="$(dirname "$(realpath "$BASH_SOURCE")")"

这样可以解析符号链接(realpath可以做到),可以处理空格(使用双引号可以做到这一点),即使在源代码(../myscript)或被其他脚本调用($ BASH_SOURCE可以处理)的情况下,也可以找到当前的脚本名称。毕竟,最好将其保存在环境变量中以供重复使用或在其他位置轻松复制(this =)...


3
FYI realpath不是内置的BASH命令。它是一个独立的可执行文件,仅在某些发行版中可用
StvnW 2014年

4

在中,bash您可以使用获取脚本文件名$0。通常$1$2等用于访问CLI参数。同样地$0,访问触发脚本的名称(脚本文件名)。

#!/bin/bash
echo "You are running $0"
...
...

如果您使用类似path的路径调用脚本,/path/to/script.sh那么$0文件名也将带有path。在这种情况下,只需要使用$(basename $0)获得脚本文件名即可。


3
echo "$(basename "`test -L ${BASH_SOURCE[0]} \
                   && readlink ${BASH_SOURCE[0]} \
                   || echo ${BASH_SOURCE[0]}`")"

2

$0没有回答这个问题(据我了解)。演示:

$ cat script.sh
#!/ bin / sh
echo`basename $ 0`
$ ./script.sh 
script.sh
$ ln script.sh链接脚本
$ ./linktoscript 
链接脚本

如何./linktoscript打印出来script.sh

[编辑]在上面的注释中,尽管@ephemient似乎是人为的,但还是有可能摆弄$0它不代表文件系统资源。OP对他想要的内容有点含糊。


在上面的答案中添加了对此的答案,但是我不同意这是我们真正想要的(或者您应该想要,除非有一些我目前无法理解的
令人沮丧的

2
我认为打印linktoscript而不是script.sh是一项功能,而不是错误。几个Unix命令使用其名称来更改其行为。一个示例是vi / ex / view。
mouviciel 2009年

@Tanktalus:在某些情况下,您希望行为根据文件的实际位置进行更改-在以前的项目中,我有一个“活动分支”脚本,该脚本会链接到我所在分支的多个工具的路径从事 这些工具然后可以找出将它们检入到哪个目录以针对正确的文件运行。
Andrew Aylett

2

信息感谢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
}

干杯


1

简短,清晰,简单 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.


0

这是我根据Dimitre Radoulov的回答(顺便说一句,我赞成)的启发得出的。

script="$BASH_SOURCE"
[ -z "$BASH_SOURCE" ] && script="$0"

echo "Called $script with $# argument(s)"

无论您如何调用脚本

. path/to/script.sh

要么

./path/to/script.sh


-6

这样的东西?

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

5
-1,不能回答问题(不显示如何查找脚本的名称),并且在一个非常有问题的脚本中是一个令人困惑的示例。
2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.