当前位置:  开发笔记 > 编程语言 > 正文

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

如何解决《Unixshell脚本找出脚本文件所在的目录?》经验,为你挑选了12个好方法。

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



1> TheMarko..:

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

#!/usr/bin/env bash

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


如果脚本在您的路径中,这将不起作用.
在bash中使用`$ BASH_SOURCE`代替`$ 0`更安全,因为`$ 0`并不总是包含被调用脚本的路径,例如''sourcing'脚本时.
如果您通过其他目录中的符号链接调用脚本,则此方法无效.为了完成这项工作,您还需要使用`readlink`(参见下面的答案)
@auraham:`CUR_PATH = $(pwd)`或`pwd`返回当前目录(不必是脚本父目录)!
我尝试使用`$ BASH_SOURCE`推荐@ mklement0方法,然后返回我需要的内容.我的脚本是从另一个脚本调用的,`$ 0`返回`.`而`$ BASH_SOURCE`返回正确的子目录(在我的例子中是`scripts`).
`$ BASH_SOURCE`是特定于Bash的,问题是关于shell脚本.
@BillLynch:这只是部分正确:路径中的一个脚本_directly_通过它的shebang行调用,_does_包含`$ 0`中脚本的_full_路径.相比之下,如果你做`bash `,它不会 - 但这种调用方法是不寻常的和不明智的(作为旁白:Bash和Ksh支持这种调用方法,Dash和Zsh不支持).无论哪种方式,`$ BASH_SOURCE`都是在Bash中使用的正确变量.(另外:答案中应该加上'$ 0`双引号.)
@thang它不打印当前目录,而是脚本的相对目录.

2> 小智..:

原始帖子包含解决方案(忽略响应,他们不添加任何有用的东西).有趣的工作是通过提到的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


`readlink`的`-f`选项在OS X(Lion)和可能的BSD上做了不同的事情.http://stackoverflow.com/questions/1055671/how-can-i-get-the-behavior-of-gnus-readlink-f-on-a-mac
注意:并非所有系统都具有`readlink`.这就是为什么我建议使用pushd/popd(内置插件用于bash).
澄清@ Ergwun的评论:OS X根本不支持`-f`(从Lion开始); 在那里你可以放弃`-f`以解决最多一个间接层,例如`pushd'$(dirname"$(readlink"$ BASH_SOURCE"|| echo"$ BASH_SOURCE")")"`,或者您可以滚动自己的递归符号链接脚本,如链接的帖子中所示.
@StefanHaberl我认为如果你在目前的工作目录与脚本的位置不同的情况下运行脚本(例如`sh /some/other/directory/script.sh),那将是一个问题,在这种情况下`.`将是你的pwd,而不是`/ some/other/directory`
我仍然不明白,为什么OP需要绝对路径.报告"." 如果你想访问相对于脚本路径的文件,你应该工作正常,你调用脚本像./myscript.sh

3> Daniel..:

之前对答案的评论说,但在所有其他答案中很容易错过.

使用bash时:

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

Bash参考手册,5.2 Bash变量


只有这一个在环境路径中使用脚本,投票最多的不起作用.谢谢!

4> Mez..:

假设你正在使用bash

#!/bin/bash

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

echo $current_dir
echo $script_dir

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

/
/home/mez

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


当我从当前目录调用脚本时,这将不起作用.

5> docwhat..:

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


您可以将`pushd` /`popd`替换为`cd $(dirname"$ {0}")`和`cd -`,以使其适用于其他shell,如果它们具有`pwd -L`.

6> ranamalo..:

正如马科所说:

BASEDIR=$(dirname $0)
echo $BASEDIR

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

要解决该问题,请使用:

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

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

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



7> Alexandro de..:

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

并且是:

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



8> blueyed..:
cd $(dirname $(readlink -f $0))



9> Ján Sáreník..:

让它成为POSIX oneliner:

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

测试了许多与Bourne兼容的shell,包括BSD.

据我所知,我是作者,我将其置于公共领域.有关详细信息,请参阅:https: //www.jasan.tk/posts/2017-05-11-posix_shell_dirname_replacement/



10> 小智..:
BASE_DIR="$(cd "$(dirname "$0")"; pwd)";
echo "BASE_DIR => $BASE_DIR"


我知道的最可靠的非打击特定方式。

11> thebunnyrule..:

介绍

这个答案纠正了这个线程(由TheMarko编写)的非常破碎但令人震惊的最高投票答案:

#!/usr/bin/env bash

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

为什么在自己的目录上不能使用目录名“ $ 0”?

dirname $ 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和目录名$ 0将给出:

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

使用方法:

sh script

如果不输入完整路径,还将给出BASEDIR =“。

使用相对目录:

 ../some/path/./script

给出$ 0的目录名:

 ../some/path/.

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

 path/./script.sh

您将获得目录名$ 0的以下值:

 path/. 

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

 ./path/.

basedir $ 0只能工作的两种情况是用户使用sh或touch启动脚本,因为这两种情况都将导致$ 0:

 $0=/some/path/script

这将为您提供可与目录名一起使用的路径。

解决方案

您需要考虑并检测上述每种情况,并在出现这种情况时对其进行修复:

#!/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> Rohith..:

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

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

这适用于linux和macOS。我看不到有人在这里提起realpath。不确定这种方法是否有任何缺点。

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

推荐阅读
无名有名我无名_593
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有