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


当前回答

这个问题的最佳答案是: 从内部获取Bash脚本的源目录

它是:

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'"

其他回答

介绍

这个答案纠正了这个帖子(由TheMarko撰写)中非常糟糕但令人震惊的投票结果:

#!/usr/bin/env bash

BASEDIR=$(dirname "$0")
echo "$BASEDIR"

为什么使用dirname "$0"对它自己不工作?

Dirname $0只在用户以非常特定的方式启动脚本时才会工作。我找到了几种这种答案失败并使脚本崩溃的情况。

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

dirname "$0"

$0表示调用脚本的命令的第一部分(它基本上是没有参数的输入命令:

/some/path/./script argument1 argument2

0美元=剧本-点- path。”

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

给出dirname $0的值:

 ../some/path/.

如果你在/some目录下,你以这种方式调用脚本(注意开头没有/,同样是一个相对路径):

 path/./script.sh

你会得到dirname $0的值:

 path/. 

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

 ./path/.

只有两种情况下basedir $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

基本版:

dir=$(dirname $0)

如果脚本可以通过$PATH调用,那么:

dir=$(dirname $(which $0))

如果脚本可能像这样调用:bash script.sh,那么:

dir=$(dirname $(which $0 2>/dev/null || realpath ./$0))

如果你感到极度不安全,那么:

dir="$(dirname -- "$(which -- "$0" 2>/dev/null || realpath -- "./$0")")"

我发现的最有效的方法之一是

#!/bin/sh
relative_dir=`perl -e 'use Cwd "realpath";$pwd = realpath(shift); $pwd =~ s/\/[^\/]*$//; print $pwd' $0`
cd $relative_dir

使用符号链接工作,并为我的许多同事工作,无论他们选择的shell类型

在Bash中,你应该像这样得到你需要的东西:

#!/usr/bin/env bash

BASEDIR=$(dirname "$0")
echo "$BASEDIR"

这一行代码告诉shell脚本在哪里,与您是否运行它或是否获取它无关。此外,它还会解析所涉及的任何符号链接,如果是这样的话:

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

顺便说一下,我认为您正在使用/bin/bash.