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

时间:2008-10-28 08:19:25

标签: shell unix

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

16 个答案:

答案 0 :(得分:476)

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

#!/usr/bin/env bash

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

答案 1 :(得分:350)

原始帖子包含解决方案(忽略响应,它们不添加任何有用的内容)。有趣的工作是通过提到的带有选项readlink的unix命令-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

答案 2 :(得分:46)

之前对答案的评论说,但很容易在所有其他答案中遗漏。

使用bash时:

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

Bash Reference Manual, 5.2 Bash Variables

答案 3 :(得分:38)

假设你正在使用bash

#!/bin/bash

current_dir=$(pwd)
script_dir=$(dirname $0)

echo $current_dir
echo $script_dir

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

/
/home/mez

请记住,在从命令输出中分配变量时,请将命令包装在$()中 - 否则您将无法获得所需的输出。

答案 4 :(得分:31)

如果你正在使用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}"

答案 5 :(得分:17)

正如马科所说:

BASEDIR=$(dirname $0)
echo $BASEDIR

除非您从脚本所在的同一目录执行脚本,否则这将起作用,在这种情况下,您将获得值'。'

要解决该问题,请使用:

current_dir=$(pwd)
script_dir=$(dirname $0)

if [ $script_dir = '.' ]
then
script_dir="$current_dir"
fi

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

答案 6 :(得分:15)

这个问题的最佳答案在这里得到了回答:
Getting the source directory of a Bash script from within

并且是:

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

One-liner将为您提供脚本的完整目录名称,无论它在何处被调用。

要了解它是如何工作的,您可以执行以下脚本:

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

答案 7 :(得分:13)

cd $(dirname $(readlink -f $0))

答案 8 :(得分:7)

BASE_DIR="$(cd "$(dirname "$0")"; pwd)";
echo "BASE_DIR => $BASE_DIR"

答案 9 :(得分:2)

blueyed’s answer启发

read < <(readlink -f $0 | xargs dirname)
cd $REPLY

答案 10 :(得分:2)

这么多答案,都是合情合理的,每个答案都有专业人士的答案,以及&amp; con&amp;略微不同的目标(可能应该为每个目标说明)。这是另一个解决方案,它满足所有系统中所有系统的清晰和工作的主要目标,所有bash(没有关于bash版本的假设,或readlinkpwd选项),并且合理地做到了你期望发生什么(例如,解决符号链接是一个有趣的问题,但通常不是你真正想要的),处理边缘情况,如路径中的空格等,忽略任何错误并使用理智如果有任何问题,请默认。

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

# 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)"

答案 11 :(得分:2)

<强>引言

这个答案 纠正 这个帖子的非常破碎但令人震惊的最高投票答案(由TheMarko撰写):

#!/usr/bin/env bash

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

为什么使用dirname为“$ 0”,因为它不能正常工作?

dirname $ 0 仅在用户以非常具体的方式启动脚本时才有效。我能够找到这种答案失败并崩溃脚本的几种情况。

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

获取脚本目录
dirname "$0"

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

/some/path/./script argument1 argument2

$ 0 = “/一些/路径/./脚本”

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目录中并且以这种方式调用脚本(注意在开头没有/,再次是相对路径):

 path/./script.sh

你会得到dirname $ 0的这个值:

 path/. 

和./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

答案 12 :(得分:2)

这个单线程告诉shell脚本在哪里,无论你运行它还是来源它都无关紧要。此外,它解决了所涉及的任何符号链接,如果是这样的话:

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

顺便说一句,我想你正在使用 / bin / bash

答案 13 :(得分:1)

如果要获取实际的脚本目录(无论您是使用符号链接还是直接调用脚本),请尝试:

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

这在linux和macOS上均可使用。我在这里看不到有人提及realpath。不确定这种方法是否有缺点。

在macOS上,您需要安装coreutils才能使用realpath。例如:brew install coreutils

答案 14 :(得分:0)

使用 tcsh,您可以使用 :h 变量修饰符来检索路径。

需要注意的是,如果脚本以 tcsh myscript.csh 的形式执行,那么您将只能获得脚本名称。一种解决方法是验证路径,如下所示。

#!/bin/tcsh

set SCRIPT_PATH = $0:h
if ( $SCRIPT_PATH == $0 ) then
        set SCRIPT_PATH = "."
endif

$SCRIPT_PATH/compile.csh > $SCRIPT_PATH/results.txt

有关变量修饰符的更多信息,请访问 https://learnxinyminutes.com/docs/tcsh/

答案 15 :(得分:-4)

应该这样做:

echo `pwd`/`dirname $0`

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