代码之家  ›  专栏  ›  技术社区  ›  William Yeung

unix shell脚本找出脚本文件所在的目录?

  •  392
  • William Yeung  · 技术社区  · 16 年前

    基本上,我需要使用与shell脚本文件位置相关的路径来运行脚本,如何将当前目录更改为与脚本文件所在的目录相同的目录?

    15 回复  |  直到 6 年前
        1
  •  450
  •   James Ko    9 年前

    在bash中,您应该得到如下所需的:

    #!/usr/bin/env bash
    
    BASEDIR=$(dirname "$0")
    echo "$BASEDIR"
    
        2
  •  340
  •   Community CDub    8 年前

    原始日志包含解决方案(忽略响应,它们不添加任何有用的内容)。有趣的工作是通过上述的unix命令完成的 readlink 有选择权 -f . 当脚本由绝对路径和相对路径调用时工作。

    对于bash、sh、ksh:

    #!/bin/bash 
    # Absolute path to this script, e.g. /home/user/bin/foo.sh
    SCRIPT=$(readlink -f "$0")
    # Absolute path this script is in, thus /home/user/bin
    SCRIPTPATH=$(dirname "$SCRIPT")
    echo $SCRIPTPATH
    

    对于TCSH,CSH:

    #!/bin/tcsh
    # Absolute path to this script, e.g. /home/user/bin/foo.csh
    set SCRIPT=`readlink -f "$0"`
    # Absolute path this script is in, thus /home/user/bin
    set SCRIPTPATH=`dirname "$SCRIPT"`
    echo $SCRIPTPATH
    

    参见: https://stackoverflow.com/a/246128/59087

        3
  •  44
  •   0az    6 年前

    早些时候对一个答案的评论说了这一点,但在所有其他答案中很容易遗漏。

    使用bash时:

    echo this file: "$BASH_SOURCE"
    echo this dir: "$(dirname "$BASH_SOURCE")"
    

    Bash Reference Manual, 5.2 Bash Variables

        4
  •  36
  •   Nathan Vērzemnieks    7 年前

    假设你在使用bash

    #!/bin/bash
    
    current_dir=$(pwd)
    script_dir=$(dirname $0)
    
    echo $current_dir
    echo $script_dir
    

    这个脚本应该打印您所在的目录,然后打印脚本所在的目录。例如,当从调用它时 / 剧本在 /home/mez/ 它的输出

    /
    /home/mez
    

    记住,当从命令输出中分配变量时,请将命令包装在 $( ) -否则就得不到所需的输出。

        5
  •  30
  •   docwhat    15 年前

    如果你在使用bash…

    #!/bin/bash
    
    pushd $(dirname "${0}") > /dev/null
    basedir=$(pwd -L)
    # Use "pwd -P" for the path without links. man bash for more info.
    popd > /dev/null
    
    echo "${basedir}"
    
        6
  •  15
  •   ranamalo    14 年前

    正如市场建议:

    BASEDIR=$(dirname $0)
    echo $BASEDIR
    

    除非您从脚本所在的同一目录执行脚本,否则这是有效的,在这种情况下,您将得到一个值“.”。

    要解决该问题,请使用:

    current_dir=$(pwd)
    script_dir=$(dirname $0)
    
    if [ $script_dir = '.' ]
    then
    script_dir="$current_dir"
    fi
    

    现在您可以在整个脚本中使用变量current_dir来引用脚本目录。但是,这可能仍然存在symlink问题。

        7
  •  14
  •   Alexandro de Oliveira    7 年前

    这个问题的最佳答案是:
    Getting the source directory of a Bash script from within

    是:

    DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
    

    一个行程序,它将为您提供脚本的完整目录名,无论从何处调用脚本。

    要了解它的工作原理,可以执行以下脚本:

    #!/bin/bash
    
    SOURCE="${BASH_SOURCE[0]}"
    while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
      TARGET="$(readlink "$SOURCE")"
      if [[ $TARGET == /* ]]; then
        echo "SOURCE '$SOURCE' is an absolute symlink to '$TARGET'"
        SOURCE="$TARGET"
      else
        DIR="$( dirname "$SOURCE" )"
        echo "SOURCE '$SOURCE' is a relative symlink to '$TARGET' (relative to '$DIR')"
        SOURCE="$DIR/$TARGET" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
      fi
    done
    echo "SOURCE is '$SOURCE'"
    RDIR="$( dirname "$SOURCE" )"
    DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
    if [ "$DIR" != "$RDIR" ]; then
      echo "DIR '$RDIR' resolves to '$DIR'"
    fi
    echo "DIR is '$DIR'"
    
        8
  •  13
  •   blueyed    14 年前
    cd $(dirname $(readlink -f $0))
    
        9
  •  7
  •   community wiki 6 revs Ján Sáreník    6 年前

    让我们把它变成一个posix一行程序:

    a="/$0"; a=${a%/*}; a=${a#/}; a=${a:-.}; BASEDIR=$(cd "$a"; pwd)
    

    在许多波恩兼容的外壳上测试,包括BSD外壳。

    据我所知,我是作者,我把它放到了公共领域。有关详细信息,请参阅: https://www.jasan.tk/posts/2017-05-11-posix_shell_dirname_replacement/

        10
  •  4
  •   kvantour    6 年前
    BASE_DIR="$(cd "$(dirname "$0")"; pwd)";
    echo "BASE_DIR => $BASE_DIR"
    
        11
  •  2
  •   Community CDub    8 年前

    受到启发 blueyed’s answer

    read < <(readlink -f $0 | xargs dirname)
    cd $REPLY
    
        12
  •  2
  •   michael    7 年前

    如此多的答案,都是合理的,每一个都有赞成和反对的目标(可能每个目标都应该说明)。这是另一个解决方案,它满足了一个主要目标,即在所有的系统上、在所有的bash上清晰地工作(不假设bash版本,或者 readlink pwd 选项),并合理地执行您期望发生的事情(例如,解决符号链接是一个有趣的问题,但通常不是您真正想要的),处理边缘情况,如路径中的空格等,忽略任何错误,如果有任何问题,则使用健全的默认值。

    每个组件都存储在单独的变量中,您可以单独使用:

    # script path, filename, directory
    PROG_PATH=${BASH_SOURCE[0]}      # this script's name
    PROG_NAME=${PROG_PATH##*/}       # basename of script (strip path)
    PROG_DIR="$(cd "$(dirname "${PROG_PATH:-$PWD}")" 2>/dev/null 1>&2 && pwd)"
    
        13
  •  2
  •   thebunnyrules    7 年前

    介绍

    这个答案 改正 这条线非常破碎但令人震惊的最高投票答案(由市场部撰写):

    #!/usr/bin/env bash
    
    BASEDIR=$(dirname "$0")
    echo "$BASEDIR"
    

    为什么在上面使用dirname“$0”不起作用?

    名字0美元 只有当用户以非常具体的方式启动脚本时才有效。我能够找到几个这样的情况:这个答案失败了,脚本崩溃了。

    首先,让我们了解这个答案是如何工作的。他正在通过

    dirname "$0"
    

    0美元 表示调用脚本的命令的第一部分(基本上是不带参数的输入命令:

    /some/path/./script argument1 argument2
    

    $0=“/some/path//script”

    dirname基本上查找字符串中的最后一个/并将其截断。所以如果你这样做:

      dirname /usr/bin/sha256sum
    

    你会得到:usr/bin

    这个例子工作得很好,因为/usr/bin/sha256sum是一个格式正确的路径,但是

      dirname "/some/path/./script"
    

    工作不好,会给你:

      BASENAME="/some/path/." #which would crash your script if you try to use it as a path
    

    假设你和你的脚本在同一个目录下,用这个命令启动它。

    ./script   
    

    在这种情况下,$0将是。/script和dirname$0将给出:

    . #or BASEDIR=".", again this will crash your script
    

    使用:

    sh script
    

    如果不输入完整路径,也会给出basedir=“”

    使用相对目录:

     ../some/path/./script
    

    给出目录名$0:

     ../some/path/.
    

    如果您在/some目录中,并且以这种方式调用脚本(请注意,开头没有/some目录,再次是相对路径):

     path/./script.sh
    

    您将以dirname$0获得此值:

     path/. 
    

    和/path//script(相对路径的另一种形式)给出:

     ./path/.
    

    只有两种情况 Basdir 0美元 如果用户使用sh或touch启动脚本,将起作用,因为两者都将导致$0:

     $0=/some/path/script
    

    这将为您提供一个可以与dirname一起使用的路径。

    解决方案

    您将解释并检测上述每一种情况,并在出现以下情况时对其应用修复:

    #!/bin/bash
    #this script will only work in bash, make sure it's installed on your system.
    
    #set to false to not see all the echos
    debug=true
    
    if [ "$debug" = true ]; then echo "\$0=$0";fi
    
    
    #The line below detect script's parent directory. $0 is the part of the launch command that doesn't contain the arguments
    BASEDIR=$(dirname "$0") #3 situations will cause dirname $0 to fail: #situation1: user launches script while in script dir ( $0=./script)
                                                                         #situation2: different dir but ./ is used to launch script (ex. $0=/path_to/./script)
                                                                         #situation3: different dir but relative path used to launch script
    if [ "$debug" = true ]; then echo 'BASEDIR=$(dirname "$0") gives: '"$BASEDIR";fi                                 
    
    if [ "$BASEDIR" = "." ]; then BASEDIR="$(pwd)";fi # fix for situation1
    
    _B2=${BASEDIR:$((${#BASEDIR}-2))}; B_=${BASEDIR::1}; B_2=${BASEDIR::2}; B_3=${BASEDIR::3} # <- bash only
    if [ "$_B2" = "/." ]; then BASEDIR=${BASEDIR::$((${#BASEDIR}-1))};fi #fix for situation2 # <- bash only
    if [ "$B_" != "/" ]; then  #fix for situation3 #<- bash only
            if [ "$B_2" = "./" ]; then
                    #covers ./relative_path/(./)script
                    if [ "$(pwd)" != "/" ]; then BASEDIR="$(pwd)/${BASEDIR:2}"; else BASEDIR="/${BASEDIR:2}";fi
            else
                    #covers relative_path/(./)script and ../relative_path/(./)script, using ../relative_path fails if current path is a symbolic link
                    if [ "$(pwd)" != "/" ]; then BASEDIR="$(pwd)/$BASEDIR"; else BASEDIR="/$BASEDIR";fi
            fi
    fi
    
    if [ "$debug" = true ]; then echo "fixed BASEDIR=$BASEDIR";fi
    
        14
  •  2
  •   Richard Gomes    6 年前

    这一行程序告诉我们shell脚本在哪里, 不管您是运行它还是从中获得它 . 此外,它还解析涉及的任何符号链接,如果是这样的话:

    dir=$(dirname $(test -L "$BASH_SOURCE" && readlink -f "$BASH_SOURCE" || echo "$BASH_SOURCE"))
    

    顺便问一下,我想你在用 /BI/BASH .

        15
  •  -4
  •   kenorb    10 年前

    这样就可以做到:

    echo `pwd`/`dirname $0`
    

    它可能看起来很难看,这取决于它是如何被调用的和CWD的,但是它应该能把你带到你需要去的地方(或者如果你关心字符串的外观,你可以调整它)。