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

2024-10-10 08:38:00
admin
原创
78
摘要:问题描述:如何确定脚本本身内部的 Bash 脚本文件的名称?比如,如果我的脚本在文件中runme.sh,那么我该如何让它显示“您正在运行 runme.sh”消息而不对其进行硬编码?解决方案 1:me=$(basename "$0") 要通过符号链接1进行读取,这通常不是您想...

问题描述:

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

比如,如果我的脚本在文件中runme.sh,那么我该如何让它显示“您正在运行 runme.sh”消息而不对其进行硬编码?


解决方案 1:

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

解决方案 2:

#  -  -  -  -  -  - - 脚本  -  -  -  -  -  - - #

#!/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 "'你好'" "'威廉'"

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

# 调用的参数 ---> 'hello there' 'william'
# $1 ----------------------> ‘你好’
# $2 ----------------------> ‘威廉’
# 到我的路径 --------------> /misc/shell_scripts/check_root/show_parms.sh
# 父路径 -------------> /misc/shell_scripts/check_root
# 我的名字 -----------------> show_parms.sh

#  -  -  -  -  -  - - 结尾  -  -  -  -  -  - - #

解决方案 3:

使用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
$BASH_SOURCE is: %s
' "$0" "$BASH_SOURCE"

解决方案 4:

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

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

$(basename $BASH_SOURCE) 

解决方案 5:

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

解决方案 6:

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

解决方案 7:

回答Chris Conway 的问题,在 Linux 上(至少)你应该这样做:

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

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

解决方案 8:

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

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 中都有相应的元素来描述调用堆栈。例如,${FUNCNAME[$i]} 是从文件 ${BASH_SOURCE[$i+1]} 的行号 ${BASH_LINENO[$i]} 调用的。caller 内置函数使用此信息显示当前调用堆栈。

[来源:Bash 手册]

解决方案 9:

由于有些评论询问没有扩展名的文件名,这里有一个如何实现的示例:

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

享受!

解决方案 10:

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

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

解决方案 11:

./self.sh这对于、~/self.shsource self.sh、都很适用source ~/self.sh

#!/usr/bin/env bash

self=$(readlink -f "${BASH_SOURCE[0]}")
basename=$(basename "$self")

echo "$self"
echo "$basename"

致谢:我结合了多个答案才得到这个。

解决方案 12:

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

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

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

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

我同意,如果您的目标是向用户提供反馈,那么取消引用符号链接会引起混淆,但有时您确实需要获取脚本或其他文件的规范名称,而在我看来,这是最好的方法。

解决方案 13:

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

这将解析符号链接(realpath 负责处理)、处理空格(双引号负责处理),并且即使在获取资源(. ./myscript)或由其他脚本调用时($BASH_SOURCE 负责处理),也会找到当前脚本名称。完成所有这些之后,最好将其保存在环境变量中以供重复使用或轻松复制到其他地方(this=)...

解决方案 14:

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

basename $0

解决方案 15:

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

解决方案 16:

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

解决方案 17:

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

解决方案 18:

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

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

如果您使用路径调用脚本/path/to/script.sh,则$0还会提供带有路径的文件名。在这种情况下,只需使用$(basename $0)即可获取脚本文件名。

解决方案 19:

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

干杯

解决方案 20:

在所有情况下获取脚本或源脚本的“真实路径”:

fullname=$(readlink $0)  # Take care of  symbolic links
dirname=${fullname%/*}       # Get (most of the time) the dirname
realpath=$(dirname $BASH_SOURCE) # TO handle sourced scripts
[ "$realpath" = '.' ] && realpath=${dirname:-.}

这里是要生成的 bash 脚本(在新创建的“workdir”子目录中以及当前目录中的“ mytest ”),该 bash 脚本反过来会生成另一个脚本,而该脚本又会调用 bash 定义的函数.... 已经用多种方式测试了启动它们:

#!/bin/bash
##############################################################

ret=0

fullname=$(readlink $0)  # Take care of  symbolic links
dirname=${fullname%/*}       # Get (most of the time) the dirname
realpath=$(dirname $BASH_SOURCE) # TO handle sourced scripts
[ "$realpath" = '.' ] && realpath=${dirname:-.}

fullname_withoutextension=${fullname%.*}

mkdir -p workdir
cat <<'EOD' > workdir/_script_.sh
#!/bin/bash
##############################################################

ret=0

fullname=$(readlink $0)  # Take care of  symbolic links
dirname=${fullname%/*}       # Get (most of the time) the dirname
realpath=$(dirname $BASH_SOURCE) # TO handle sourced scripts
[ "$realpath" = '.' ] && realpath=${dirname:-.}

fullname_withoutextension=${fullname%.*}

echo
echo "# ------------- RESULTS ------------- #"
echo "# path to me ($0)----------->  ${0}     "
echo "# arguments called with ---->  ${@}     "
echo "# $1 ----------------------->  $1       "
echo "# $2 ----------------------->  $2       "
echo "# path to me ($fullname)---->  ${fullname} "
echo "# parent path(${0%/*})------>  ${0%/*}  "
echo "# parent path($dirname)----->  ${dirname} "
echo "# my name ----${0##*/}------>  ${0##*/} "
echo "# my source -${BASH_SOURCE}->  ${BASH_SOURCE} "
echo "# parent path(from BASH_SOURCE) -> $(dirname $BASH_SOURCE)"
echo "# my function name -${FUNCNAME[0]}------>  ${FUNCNAME[0]}"
echo "# my source or script real path (realpath)------------------>  $realpath"
echo
[ "$realpath" = "workdir" ] || ret=1
[ $ret = 0 ] || echo "*******************************************************"
[ $ret = 0 ] || echo "***********   ERROR  **********************************"
[ $ret = 0 ] || echo "*******************************************************"

show_params () {
        echo
        echo "# --- RESULTS FROM show_params() ---- #"
        echo "# path to me ($0)----------->  ${0}     "
        echo "# arguments called with ---->  ${@}     "
        echo "# $1 ----------------------->  $1       "
        echo "# $2 ----------------------->  $2       "
        echo "# path to me ($fullname)---->  ${fullname} "
        echo "# parent path(${0%/*})------>  ${0%/*}  "
        echo "# parent path($dirname)----->  ${dirname} "
        echo "# my name ----${0##*/}------>  ${0##*/} "
        echo "# my source -${BASH_SOURCE}->  ${BASH_SOURCE} "
        echo "# parent path(from BASH_SOURCE) -> $(dirname $BASH_SOURCE)"
        echo "# my function name -${FUNCNAME[0]}------>  ${FUNCNAME[0]}"
        echo "# my source or script real path (realpath)------------------>  $realpath"
        echo
        [ "$realpath" = "workdir" ] || ret=1
        [ $ret = 0 ] || echo "*******************************************************"
        [ $ret = 0 ] || echo "***********   ERROR  **********************************"
        [ $ret = 0 ] || echo "*******************************************************"

}
show_params "$@"

EOD

cat workdir/_script_.sh > workdir/_side_by_side_script_sourced.inc

cat <<'EOD' >> workdir/_script_.sh

echo "# . $realpath/_side_by_side_script_sourced.inc 'hello there' 'william'"
. $realpath/_side_by_side_script_sourced.inc 'hello there' 'william'

[ $ret = 0 ] || echo "*******************************************************"
[ $ret = 0 ] || echo "***********   ERROR  **********************************"
[ $ret = 0 ] || echo "*******************************************************"
EOD

chmod +x  workdir/_script_.sh
[ -L _mytest_ ] && rm _mytest_
ln -s workdir/_script_.sh _mytest_

# ------------- CALLED ------------- #

called_by () {
        echo '=========================================================================='
        echo " Called by : " "$@"
        echo '=========================================================================='
        eval "$@"
}

called_by bash _mytest_
called_by ./_mytest_

called_by bash workdir/_script_.sh
called_by workdir/_script_.sh
called_by . workdir/_script_.sh


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

echo
echo
[ $ret = 0 ] || echo "*******************************************************"
[ $ret = 0 ] || echo "***********   ERROR  **********************************"
[ $ret = 0 ] || echo "*******************************************************"
echo
[ $ret = 0 ] && echo ".... location of scripts ($realpath) should always be equal to $realpath, for all test cases at date".
echo

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

解决方案 21:

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

我从另一个 Stack Overflow 问题“Bash 脚本能否告诉您它存储在哪个目录中?”中获得了上述内容,但我认为它对于这个主题也很有用。

解决方案 22:

这是我受到Dimitre Radoulov的回答(顺便说一句,我投了赞成票)启发而想到的。

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

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

无论你如何调用脚本

. path/to/script.sh

或者

./path/to/script.sh

解决方案 23:

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

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

然后像这样从终端运行

./file_name.sh

解决方案 24:

作为以上答案中未列出的选项,您可以使用脚本的 pid 从文件夹中获取大量信息(包括名称)/proc/$pid,如下所示:

#!/bin/bash

pid=$$ # get script's pid to var $pid

echo $pid

# there is a file in /proc/$pid folder called cmdline
# this file stores full command line used by script
cat /proc/$pid/cmdline

输出:

$ ./test 1 '2 3' 4 
12924
/bin/bash./test12 34

输出看起来有点混乱,它只是一个字符串......不完全是它实际上有分隔符,但为了能够看到它们,我们需要cat像这样更改我们的命令:

#!/bin/bash

pid=$$ # get script's pid to var $pid

echo $pid

# add -A - show all option for cat
cat -A /proc/$pid/cmdline

好了,现在我们可以看到分隔符^@

$ ./test 1 '2 3' 4 
13002
/bin/bash^@./test^@1^@2 3^@4^@

让我们添加更多黑客手段来获取名称:

#!/bin/bash

pid=$$ # get script's pid to var $pid

# tr to change '^@' into ' ' and awk to print 2nt column(script name)
name=$(cat -A /proc/$pid/cmdline | tr '^@' ' ' | awk '{print $2}')

echo "
pid:  $pid
name: $name
"

结果:

$ ./test 1 '2 3' 4 

pid:  13091
name: ./test

解决方案 25:

echo "您正在运行 $0"

解决方案 26:

像这样的东西?

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
相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   601  
  华为IPD与传统研发模式的8大差异在快速变化的商业环境中,产品研发模式的选择直接决定了企业的市场响应速度和竞争力。华为作为全球领先的通信技术解决方案供应商,其成功在很大程度上得益于对产品研发模式的持续创新。华为引入并深度定制的集成产品开发(IPD)体系,相较于传统的研发模式,展现出了显著的差异和优势。本文将详细探讨华为...
IPD流程是谁发明的   7  
  如何通过IPD流程缩短产品上市时间?在快速变化的市场环境中,产品上市时间成为企业竞争力的关键因素之一。集成产品开发(IPD, Integrated Product Development)作为一种先进的产品研发管理方法,通过其结构化的流程设计和跨部门协作机制,显著缩短了产品上市时间,提高了市场响应速度。本文将深入探讨如...
华为IPD流程   9  
  在项目管理领域,IPD(Integrated Product Development,集成产品开发)流程图是连接创意、设计与市场成功的桥梁。它不仅是一个视觉工具,更是一种战略思维方式的体现,帮助团队高效协同,确保产品按时、按质、按量推向市场。尽管IPD流程图可能初看之下显得错综复杂,但只需掌握几个关键点,你便能轻松驾驭...
IPD开发流程管理   8  
  在项目管理领域,集成产品开发(IPD)流程被视为提升产品上市速度、增强团队协作与创新能力的重要工具。然而,尽管IPD流程拥有诸多优势,其实施过程中仍可能遭遇多种挑战,导致项目失败。本文旨在深入探讨八个常见的IPD流程失败原因,并提出相应的解决方法,以帮助项目管理者规避风险,确保项目成功。缺乏明确的项目目标与战略对齐IP...
IPD流程图   8  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用