关于Linux:我如何知道bash脚本中的脚本文件名?

How do I know the script file name in a Bash script?

如何确定脚本本身中bash脚本文件的名称?

就像我的脚本在文件runme.sh中一样,那么如何才能在不进行硬编码的情况下显示"you are running runme.sh"消息?


1
me=`basename"$0"`

对于通过symlink1进行读取,这通常不是您想要的(您通常不想这样混淆用户),请尝试:

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

IMO,这会产生令人困惑的输出。"我跑了foo.sh,但它说我在跑bar.sh!"一定是个虫子!"此外,具有不同名称的symlinks的目的之一是基于它所称的名称提供不同的功能(在某些平台上考虑gzip和gunzip)。

1也就是说,要解析symlinks,以便当用户执行foo.sh,这实际上是到bar.sh的symlink时,您希望使用解析名称bar.sh,而不是foo.sh


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# ------------- SCRIPT ------------- #
<wyn>
#!/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
</wyn>
# ------------- CALLED ------------- #

# Notice on the next line, the first argument is called within double,
# and single quotes, since it contains two words

$  /misc/shell_scripts/check_root/show_parms.sh"'hello there'""'william'"

# ------------- RESULTS ------------- #

# arguments called with --->  'hello there' 'william'
# $1 ---------------------->  'hello there'
# $2 ---------------------->  'william'
# path to me -------------->  /misc/shell_scripts/check_root/show_parms.sh
# parent path ------------->  /misc/shell_scripts/check_root
# my name ----------------->  show_parms.sh

# ------------- END ------------- #


对于bash>=3,以下工作:

1
2
3
4
5
6
7
8
9
10
11
12
13
$ ./s
0 is: ./s
BASH_SOURCE is: ./s
$ . ./s
0 is: bash
BASH_SOURCE is: ./s

$ cat s
#!/bin/bash

printf '$0 is: %s
$BASH_SOURCE is: %s
'
"$0""$BASH_SOURCE"


如果脚本名中有空格,则更健壮的方法是使用"$0""$(basename"$0")",或者在macos上使用"$(basename \"$0\")"。这可以防止名称以任何方式被损坏或解释。一般来说,在shell中总是用双引号括变量名是一种很好的做法。


在寻找脚本时,$BASH_SOURCE给出了正确的答案。

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

1
$(basename $BASH_SOURCE)


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


要回答ChrisConway,在Linux(至少)上,您可以这样做:

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

readlink输出符号链接的值。如果不是符号链接,则打印文件名。-n告诉它不要打印换行符。-F告诉它完全遵循链接(如果一个符号链接是指向另一个链接的链接,它也会解析该链接)。


我发现这一行总是有效的,不管文件是源文件还是作为脚本运行。

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

如果您想使用symlinks,可以在上面的路径上使用readlink,递归或非递归。

使用BASH_SOURCE环境变量及其关联的FUNCNAME解释了一条直线运行的原因。

BASH_SOURCE

An array variable whose members are the source filenames where the corresponding shell function names in the FUNCNAME array variable are defined. The shell function ${FUNCNAME[$i]} is defined in the file ${BASH_SOURCE[$i]} and called from ${BASH_SOURCE[$i+1]}.

FUNCNAME

An array variable containing the names of all shell functions currently in the execution call stack. The element with index 0 is the name of any currently-executing shell function. The bottom-most element (the one with the highest index) is"main". This variable exists only when a shell function is executing. Assignments to FUNCNAME have no effect and return an error status. If FUNCNAME is unset, it loses its special properties, even if it is subsequently reset.

This variable can be used with BASH_LINENO and BASH_SOURCE. Each element of FUNCNAME has corresponding elements in BASH_LINENO and BASH_SOURCE to describe the call stack. For instance, ${FUNCNAME[$i]} was called from the file ${BASH_SOURCE[$i+1]} at line number ${BASH_LINENO[$i]}. The caller builtin displays the current call stack using this information.

[来源:bash手册]


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

这是一个边缘案例,不应该太认真。如果直接从另一个脚本(不带"source")运行该脚本,则使用$0将有效。


回复:Tanktalus的(接受的)以上回答,稍微干净一点的方法是使用:

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

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

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

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


如果调用shell脚本

1
/home/mike/runme.sh

0美元是全名

1
 /home/mike/runme.sh

base name$0将获取基本文件名

1
 runme.sh

你需要把这个基本名称放到一个变量里,比如

1
filename=$(basename $0)

并添加您的附加文本

1
echo"You are running $filename"

所以你的剧本就像

1
2
3
4
/home/mike/runme.sh
#!/bin/bash
filename=$(basename $0)
echo"You are running $filename"

由于一些注释询问了不带扩展名的文件名,下面是一个如何实现这一点的示例:

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

享受!


您可以使用$0确定脚本名(带完整路径)-要获取脚本名,只能使用

1
basename $0

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

这将解析符号链接(realpath这样做),处理空格(双引号这样做),并且即使在源代码(.或由其他脚本调用($bash_source处理)。毕竟,最好将它保存在一个环境变量中,以便重用或在其他地方轻松复制(this=)…


bash中,可以使用$0获得脚本文件名。一般来说,$1$2等用于访问cli参数。类似地,$0是访问触发脚本的名称(脚本文件名)。

1
2
3
4
#!/bin/bash
echo"You are running $0"
...
...

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


感谢比尔·埃尔南德斯。我添加了一些我正在采用的偏好。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/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
2
3
echo"$(basename"`test -L ${BASH_SOURCE[0]} \
                   && readlink ${BASH_SOURCE[0]} \
                   || echo ${BASH_SOURCE[0]}`")"

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

1
2
3
4
5
6
7
8
$ cat script.sh
#! /bin/sh
echo `basename $0`
$ ./script.sh
script.sh
$ ln script.sh linktoscript
$ ./linktoscript
linktoscript

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

[编辑]上述注释中的per@ephemient,尽管符号链接似乎是人为设计的,但也可以修改$0,使其不代表文件系统资源。手术对他想要什么有点模棱两可。


这就是我的想法,受到迪米特·拉多洛夫回答的启发(顺便说一下,我投了反对票)。

1
2
3
4
script="$BASH_SOURCE"
[ -z"$BASH_SOURCE" ] && script="$0"

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

不管你怎么称呼你的剧本

1
. path/to/script.sh

1
./path/to/script.sh


echo"您正在运行$0"


1
DIRECTORY=$(cd `dirname $0` && pwd)

我从另一个堆栈溢出问题得到了上面的内容,bash脚本能否告诉它存储在哪个目录中?但是我认为它对这个主题也很有用。


像这样的事?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
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