ChatGPT解决这个技术问题 Extra ChatGPT

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

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

就像我的脚本在文件 runme.sh 中一样,我如何让它显示“您正在运行 runme.sh”消息而不进行硬编码?

类似的[bash 脚本可以告诉它存储在哪个目录吗?](到 stackoverflow.com/questions/59895/…

B
BeeOnRope
me=`basename "$0"`

要通过 symlink1 阅读,这通常不是您想要的(您通常不想以这种方式混淆用户),请尝试:

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

IMO,这会产生令人困惑的输出。 “我运行了 foo.sh,但它说我在运行 bar.sh!?一定是一个错误!”此外,使用不同名称的符号链接的目的之一是根据它的名称提供不同的功能(想想某些平台上的 gzip 和 gunzip)。

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


$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,否则它将是一个很长的脚本,我不会在这里写。
B
Bill Hernandez

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

@NickC 见 Substring Removal
我如何只获得 show_params,即没有任何可选扩展名的名称?
如果从另一个文件夹调用脚本,则无法正常工作。该路径包含在 ${0##*/} 中。使用 GitBash 进行测试。
上面的 .bash_login 脚本对我不起作用,但 $BASH_SOURCE 的 Dimitre Radoulov 解决方案效果很好。
@AlikElzin-kilaka 对我来说,5 年后,使用本机 bash 5.0-4,它可以工作,即使从另一个文件夹调用脚本也是如此。
u
user

使用 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 中,不是吗?
k
kenorb

$BASH_SOURCE 在获取脚本时给出了正确答案。

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

$(basename $BASH_SOURCE) 

恕我直言,这个答案是最好的答案,因为解决方案使用了自记录代码。 $BASH_SOURCE 是完全可以理解的,无需阅读任何文档,而例如 ${0##*/} 则不是
我相信这个答案更有价值,因为如果我们像 . <filename> [arguments] 那样运行,$0 会给出调用者 shell 的名称。至少在 OSX 上是肯定的。
@AndreasM.Oberheim basename 的缺点是必须分叉一个单独的进程,而 ##*/ 只是 bash 自己的处理。不过,您仍然可以使用 $BASH_SOURCE${BASH_SOURCE[0]##*/}
A
Andrew Faulkner

如果脚本名称中有空格,更可靠的方法是使用 "$0""$(basename "$0")" - 或在 MacOS 上:"$(basename \"$0\")"。这可以防止名称以任何方式被破坏或解释。通常,在 shell 中始终用双引号引起变量名是一种很好的做法。


+1 直接回答 + 简洁。如果需要符号链接功能,我建议您查看:Travis B. Hartwell's answer。
M
Mr. Muskrat

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


如果我想要它没有任何可选的文件扩展名怎么办?
要删除扩展名,您可以尝试“${VARIABLE%.ext}”,其中 VARIABLE 是您从 ${0##*/} 获得的值,“.ext”是您要删除的扩展名。
C
Community

要回答 Chris Conway,在 Linux 上(至少)你会这样做:

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

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


-n 是无害的,但不是必需的,因为 $(...) 结构会修剪它。
g
gkb0986

我发现这一行总是有效的,无论文件是作为脚本获取还是作为脚本运行。

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

如果您想遵循符号链接,请在上面的路径上使用 readlink 递归或非递归。

单线工作的原因可以通过使用 BASH_SOURCE 环境变量及其关联的 FUNCNAME 来解释。

BASH_SOURCE 一个数组变量,其成员是源文件名,其中定义了 FUNCNAME 数组变量中的相应 shell 函数名称。 shell 函数 ${FUNCNAME[$i]} 在文件 ${BASH_SOURCE[$i]} 中定义并从 ${BASH_SOURCE[$i+1]} 调用。 FUNCNAME 一个数组变量,包含当前在执行调用堆栈中的所有 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 的路径。但是,如果您编写一个包含 source a 的脚本 b 并运行 ./b,它将返回 b 的路径。
您的答案不是给出第一个文件而不是最后一个/最新的文件吗?我发现 ${BASH_SOURCE[0]}" 或只是 $BASH_SOURCE 告诉我当前文件 - 即@Zainka 发布的答案。
S
Simon Mattes

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

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

享受!


这是黑魔法!
J
Jim Dodd

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

这是一个边缘情况,不应太认真。如果您直接从另一个脚本运行脚本(没有“源”),则使用 $0 将起作用。


你有一个很好的观点。不是边缘情况 IMO。不过有一个解决方案:请参阅 Dimitre Radoulov's answer above
s
simon

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

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

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

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

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


感谢sourced 脚本名称。
j
jcalfee314
this="$(dirname "$(realpath "$BASH_SOURCE")")"

这将解析符号链接(realpath 执行此操作),处理空格(双引号执行此操作),并且即使在来源(. ./myscript)或由其他脚本调用($BASH_SOURCE 处理)时也会找到当前脚本名称。毕竟,最好将其保存在环境变量中以供重复使用或在其他地方轻松复制(this=)...


仅供参考 realpath 不是内置 BASH 命令。它是一个独立的可执行文件,仅在某些发行版中可用
可以确认,在我的 14.04 框中它不可用,我不得不改用 readlink
V
VolkA

您可以使用 $0 来确定您的脚本名称(带有完整路径) - 要获取脚本名称,您可以使用修剪该变量

basename $0

L
LawrenceLi

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

N
Nishant

这适用于 ./self.sh~/self.shsource self.shsource ~/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}") 无论如何都有效。
e
ecwpz91
echo "$(basename "`test -L ${BASH_SOURCE[0]} \
                   && readlink ${BASH_SOURCE[0]} \
                   || echo ${BASH_SOURCE[0]}`")"

r
rashok

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

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

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


B
Bơ Loong A Nhứi

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

l
linxuser

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

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

干杯


S
Salathiel Genèse

这是我在 Dimitre Radoulov 的回答(顺便说一下,我赞成)的启发下得出的结论。

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

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

无论您调用脚本的方式如何

. path/to/script.sh

或者

./path/to/script.sh

A
Ali Yar Khan

$0 将给出您正在运行的脚本的名称。创建一个脚本文件并添加以下代码

#!/bin/bash
echo "Name of the file is $0"

然后像这样从终端运行

./file_name.sh

m
mmacaulay

echo "你正在运行 $0"


不是 bash 脚本,是完整路径。
C
Community
DIRECTORY=$(cd `dirname $0` && pwd)

我从另一个 Stack Overflow 问题 Can a Bash script tell what directory it's stored in? 中得到了上述内容,但我认为它对这个主题也很有用。


h
hynt

像这样的东西?

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,不回答问题(不显示如何找到脚本的名称),并且在一个非常错误的脚本中是一个令人困惑的示例。