【问题标题】:How do I know the script file name in a Bash script?我如何知道 Bash 脚本中的脚本文件名?
【发布时间】:2010-09-16 13:53:55
【问题描述】:

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

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

【问题讨论】:

标签: linux bash shell scripting


【解决方案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 将起作用。

    【讨论】:

    【解决方案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)
    

    如果您的目标是向用户提供反馈,我同意取消引用符号链接会造成混淆,但有时您确实需要将规范名称获取到脚本或其他文件,这是最好的方法,海事组织。

    【讨论】:

    • 感谢sourced 脚本名称。
    【解决方案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.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}") 无论如何都可以使用。
                  【解决方案24】:

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

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

                  然后像这样从终端运行

                  ./file_name.sh
                  

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 2011-02-17
                    • 1970-01-01
                    • 2014-10-08
                    • 2010-10-17
                    • 2011-01-30
                    • 1970-01-01
                    • 2014-07-17
                    相关资源
                    最近更新 更多