从脚本本身获取Bash脚本的源目录

如何获取其中的目录路径的Bash脚本位于,里面那个脚本?

例如,假设我想使用Bash脚本作为另一个应用程序的启动器.我想将工作目录更改为Bash脚本所在的目录,因此我可以对该目录中的文件进行操作,如下所示:

$ ./application

回答

#!/bin/bash
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"

是一个有用的单行程序,它将为您提供脚本的完整目录名称,无论它在何处被调用.

只要用于查找脚本的路径的最后一个组件不是符号链接(目录链接正常),它就会起作用.如果您还想解决脚本本身的任何链接,则需要一个多行解决方案:

#!/bin/bash
SOURCE="${BASH_SOURCE[0]}"
while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )"
SOURCE="$(readlink "$SOURCE")"
[[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
done
DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )"

这最后一个将与别名的任意组合工作,source,bash -c,符号链接等.

注意:如果您cd在运行此代码段之前访问其他目录,结果可能不正确!

另外,如果用户巧妙地重写了cd以将输出重定向到stderr(包括转义序列,例如在Mac上调用时),请注意$CDPATH陷阱和stderr输出副作用update_terminal_cwd >&2.>/dev/null 2>&1cd命令结束时添加将兼顾两种可能性.

要了解它的工作原理,请尝试运行此更详细的表单:

#!/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" )" >/dev/null 2>&1 && pwd )"
if [ "$DIR" != "$RDIR" ]; then
echo "DIR '$RDIR' resolves to '$DIR'"
fi
echo "DIR is '$DIR'"

它将打印如下:

SOURCE './scriptdir.sh' is a relative symlink to 'sym2/scriptdir.sh' (relative to '.')
SOURCE is './sym2/scriptdir.sh'
DIR './sym2' resolves to '/home/ubuntu/dotfiles/fo fo/real/real1/real2'
DIR is '/home/ubuntu/dotfiles/fo fo/real/real1/real2'
  • 这个接受的答案是不行的,它不适用于符号链接,而且过于复杂.````dirname $(readlink -f $ 0)````是正确的命令.有关测试用例,请参阅https://gist.github.com/tvlooy/cbfbdb111a4ebad8b93e
  • @tvlooy IMO你的答案也不是很好,因为当路径中有空格时它会失败.与换行符相比,这不太可能,甚至不常见.`dirname"$(readlink -f"$ 0")"`不会增加复杂性,并且对于最小的麻烦来说,公平的措施更加健壮.
  • 您可以将此方法与user25866的答案融合,以获得与`source <script>`和`bash <script>`一起使用的解决方案:`DIR ="$(cd -P"$(dirname"$ {BASH_SOURCE [ 0]}")"&& pwd)"`.
  • 有时`cd`会给STDOUT打印一些东西!例如,如果你的`$ CDPATH`有`.`.要覆盖这种情况,请使用`DIR ="$(cd"$(dirname"$ {BASH_SOURCE [0]}")">/dev/null && pwd)"`
  • 请注意,在版本bash-3.0-alpha中添加了BASH_SOURCE以进行调试.因此,如果您正在使用遗留系统,这将无法正常工作.
  • 为了处理相对的符号链接,我发现我需要`DIR ="$(cd"$(dirname"$ {BASH_SOURCE [0]}")"&& cd -P"$(dirname"$ SOURCE")"&& pwd) "`
  • @ChrisQuenelle:使用`source`时,[`$ {BASH_SOURCE [0]}`与`$ 0`不同(http://www.cyberciti.biz/faq/unix-linux-script-sourced-by-bash-can - 它 - 确定 - 它 - 自己的定位/).
  • 为什么不使用以下命令?`DIR = $(dirname - "$(readlink -f - "$ {BASH_SOURCE [0]}")")`
  • @tvlooy你的评论不是macOS(或者可能是一般的BSD)兼容,而接受的答案是.`readlink -f $ 0`给`readlink:illegal option -f`.
  • *在对命令替换的输出或变量引用的输出进行变量赋值时,没有任何理由使用引号*.证明:运行`x ="我的名字是ryran"`然后`a = $ x`或`a = $ {x/ryran/bob jones}`或`a = $(echo $ x)`
  • 这在使用source/path/scriptname时不起作用
  • @ x-yuri - 仅供参考,"readlink"在OSX上的当前表单中不可用,因此如果您尝试使用Bash在Linux和OSX上编写半跨平台脚本,这种方法最好.
  • 按照惯例,环境变量(`PATH`,`EDITOR`,`SHELL`,...)和内部shell变量(`BASH_VERSION`,`RANDOM`,...)是完全大写的.所有其他变量名称应为小写.由于变量名称区分大小写,因此该约定避免意外地覆盖环境和内部变量.
  • 对于最后一次升级,请尝试以下方法:DIR ="$(cd -P"$(dirname"$ 0")"&& pwd)"---这将为您提供绝对的解引用路径,即解析所有符号链接.
  • 根据 [ss64 的文档](http://ss64.com/bash/pwd.html),`pwd` 需要一个 `-P` 参数。描述指出`-P` 参数确保路径不包含符号链接。这个参数可以用来跟踪符号链接而不是循环吗?
  • 作为旁注:根据惯例,环境变量(PATH,EDITOR,SHELL,...)和内部shell变量(BASH_VERSION,RANDOM,...)是完全大写的.所有其他变量名称应为小写.由于变量名称区分大小写,因此该约定避免意外地覆盖环境和内部变量.
  • 似乎没有人遇到更改工作目录而不将其放回原处的问题。:( 编辑:我糟糕的`$()` 创建了一个新的shell,不是吗?
  • 因此,@ tvlooy,如果我在macOS上运行,您的解决方案是安装软件包,以便我使用您的答案?这比接受的答案的复杂性要差得多(假设没有符号链接)。

用途dirname "$0":

#!/bin/bash
echo "The script you are running has basename `basename "$0"`, dirname `dirname "$0"`"
echo "The present working directory is `pwd`"

pwd如果您没有从包含它的目录运行脚本,则单独使用将不起作用.

[matt@server1 ~]$ pwd
/home/matt
[matt@server1 ~]$ ./test2.sh
The script you are running has basename test2.sh, dirname .
The present working directory is /home/matt
[matt@server1 ~]$ cd /tmp
[matt@server1 tmp]$ ~/test2.sh
The script you are running has basename test2.sh, dirname /home/matt
The present working directory is /tmp
  • @Darron:但是因为这个问题被标记为'bash`而hash-bang行明确提到`/ bin/bash`我会说依赖bashisms是非常安全的.
  • +1,但使用`dirname $ 0`的问题是,如果目录是当前目录,你将获得`.`.这很好,除非您要更改脚本中的目录并期望使用从"dirname $ 0"获得的路径,就像它是绝对的一样.获取绝对路径:`pushd `dirname $ 0 `>/dev/null`,`SCRIPTPATH = `pwd ``,`popd>/dev/null`:http://pastie.org/1489386(但是*当然*有更好的方法来扩展这条道路吗?)
  • 对于超出bash的可移植性,$ 0可能并不总是足够的.如果在路径上找到命令,您可能需要替换"type -p $ 0"才能使其工作.
  • @Darron:如果脚本是可执行的,你只能使用`type -p`.如果使用`bash test2.sh`执行脚本,并且在其他地方有另一个同名可执行脚本,这也可以打开一个微妙的漏洞.
  • @TJ Crowder我不确定`dirname $ 0`是一个问题,如果你将它分配给一个变量,然后用它来启动像`$ dir/script.sh`这样的脚本; 我想象这是90%的时间用于此类事情的用例.`./ script.sh`可以正常工作.
  • `$ 0`在源脚本(包括`bashrc`)中给出了错误的结果
  • @TJ Crowder>但肯定有更好的方法来扩展这条道路?是的,您可以使用$(readlink -f"$ 0")以规范形式获取完整路径并解决所有符号链接.解决了大量问题.
  • @matt b:正如我所说,只要使用它的脚本不更改当前目录就可以了。如果你的脚本是这样,那么你尝试启动 `./script.sh` 并且 `script.sh` 应该和原来的在同一个目录下,它会失败,因为你已经不在了。另见:http://stackoverflow.com/questions/4774054/reliable-way-to-get-the-full-path-to-a-bash-script
  • 即使使用`bash`,`$0` 也并不总是足够的。一个脚本可能来自另一个脚本!
  • @TJCrowder @ VolodymyrM.Lisivka你也可以使用`$(realpath"$ 0")`但请注意,readlink和readpath都不适用于Mac OSX.http://stackoverflow.com/questions/3572030/bash-script-absolute-path-with-osx

dirname命令是最基本的,只需解析直到$ 0(脚本名称)变量的文件名的路径:

dirname "$0"

但是,正如matt b指出的那样,返回的路径会有所不同,具体取决于脚本的调用方式.pwd没有完成这项工作,因为它只告诉你当前目录是什么,而不是脚本所在的目录.另外,如果执行了一个脚本的符号链接,你将获得一个(可能是相对的)路径到链接所在的位置,而不是实际的脚本.

其他一些人提到了readlink命令,但最简单的是,您可以使用:

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

readlink将脚本路径解析为文件系统根目录的绝对路径.因此,任何包含单点或双点,波浪线和/或符号链接的路径都将被解析为完整路径.

这是一个演示这些内容的脚本,whatdir.sh:

#!/bin/bash
echo "pwd: `pwd`"
echo "$0: $0"
echo "basename: `basename $0`"
echo "dirname: `dirname $0`"
echo "dirname/readlink: $(dirname $(readlink -f $0))"

使用相对路径在我的主目录中运行此脚本:

>>>$ ./whatdir.sh
pwd: /Users/phatblat
$0: ./whatdir.sh
basename: whatdir.sh
dirname: .
dirname/readlink: /Users/phatblat

同样,但使用脚本的完整路径:

>>>$ /Users/phatblat/whatdir.sh
pwd: /Users/phatblat
$0: /Users/phatblat/whatdir.sh
basename: whatdir.sh
dirname: /Users/phatblat
dirname/readlink: /Users/phatblat

现在改变目录:

>>>$ cd /tmp
>>>$ ~/whatdir.sh
pwd: /tmp
$0: /Users/phatblat/whatdir.sh
basename: whatdir.sh
dirname: /Users/phatblat
dirname/readlink: /Users/phatblat

最后使用符号链接来执行脚本:

>>>$ ln -s ~/whatdir.sh whatdirlink.sh
>>>$ ./whatdirlink.sh
pwd: /tmp
$0: ./whatdirlink.sh
basename: whatdirlink.sh
dirname: .
dirname/readlink: /Users/phatblat
  • 小心引用一切以避免空白问题:`export SCRIPT_DIR ="$(dirname"$(readlink -f"$ 0")")"`
  • 默认安装中某些平台无法使用`readlink`.尽可能避免使用它
  • 在OSX Yosemite 10.10.1中,`-f`不被认为是`readlink`的选项.使用`stat -f`代替完成工作.谢谢
  • 在OSX中,有`greadlink`,它基本上是我们都熟悉的`readlink`.这是一个独立于平台的版本:`dir = `greadlink -f $ {BASH_SOURCE [0]} || readlink -f $ {BASH_SOURCE [0]} ``
  • 好的电话,@ robert.仅供参考,`greadlink`可以通过自制软件轻松安装:`brew install coreutils`
  • 注意,如果文件是源文件,则$ 0不起作用。您将获得-bash而不是脚本名称。

pushd . > /dev/null
SCRIPT_PATH="${BASH_SOURCE[0]}"
if ([ -h "${SCRIPT_PATH}" ]); then
while([ -h "${SCRIPT_PATH}" ]); do cd `dirname "$SCRIPT_PATH"`;
SCRIPT_PATH=`readlink "${SCRIPT_PATH}"`; done
fi
cd `dirname ${SCRIPT_PATH}` > /dev/null
SCRIPT_PATH=`pwd`;
popd  > /dev/null

适用于所有版本,包括

  • 通过多深度软链接调用时,
  • 当它的文件
  • 当脚本通过命令" source"aka .(点)运算符调用时.
  • $0从调用者修改arg时.
  • "./script"
  • "/full/path/to/script"
  • "/some/path/../../another/path/script"
  • "./some/folder/script"

或者,如果bash脚本本身是一个相对符号链接,跟随它并返回链接到脚本的完整路径:

pushd . > /dev/null
SCRIPT_PATH="${BASH_SOURCE[0]}";
if ([ -h "${SCRIPT_PATH}" ]) then
while([ -h "${SCRIPT_PATH}" ]) do cd `dirname "$SCRIPT_PATH"`; SCRIPT_PATH=`readlink "${SCRIPT_PATH}"`; done
fi
cd `dirname ${SCRIPT_PATH}` > /dev/null
SCRIPT_PATH=`pwd`;
popd  > /dev/null

SCRIPT_PATH无论如何被称为全路径.
只需确保在脚本开头找到它.

此评论和代码Copyleft,GPL2.0或更高版本或CC-SA 3.0(CreativeCommons Share Alike)或更高版本下的可选许可证.(c)2008年.保留所有权利.没有任何形式的保证.你被警告了.
http://www.gnu.org/licenses/gpl-2.0.txt
http://creativecommons.org/licenses/by-sa/3.0/
18eedfe1c99df68dc94d4a94712a71aaa8e1e9e36cacf421b9463dd2bbaa02906d0d6656

  • `readlink -f`是GNU特有的.BSD`readlink`没有该选项.
  • 一个脚本从其当前目录中"cd"出来是很危险的,希望以后能够再次使用`cd`:脚本可能没有权限将目录更改回调用它时的当前目录.(同样适用于pushd/popd)
  • 而不是使用pushd ...; 使用$(cd`dirname"$ {SCRIPT_PATH}"`&& pwd)不是更好吗?但无论如何伟大的剧本!
  • 太好了!可以通过SCRIPT_PATH =`readlink -f $(dirname"$ {VIRTUAL_ENV}")替换"pushd [...] popd/dev/null".
  • 上面处理的案例列表(`适用于所有版本`)有一项 == `when the file it`。任何人都可以适当地完成那个句子片段吗?
  • 所有不必要的子弹都有什么用?`([...])`效率低于`[...]`,并且没有利用隔离提供的优势来回报这里的性能.

简短回答:

`dirname $0`

或(最好):

$(dirname "$0")
  • @vidstige:`$ {BASH_SOURCE [0]}`而不是`$ 0`将与`source my/script.sh`一起使用
  • 如果您获取脚本,它将无法工作."source my/script.sh"
  • 什么都没有

您可以使用$ BASH_SOURCE

#!/bin/bash
scriptdir=`dirname "$BASH_SOURCE"`

请注意,您需要使用#!/ bin/bash而不是#!/ bin/sh,因为它是一个bash扩展名

  • 当我执行`./ foo/script`时,`$(dirname $ BASH_SOURCE)`是`./ foo`.
  • @Till, In this case we can use `realpath` command to get full path of ./foo/script. So `dirname $(realpath ./foo/script) ` will give the path of script.

这应该这样做:

DIR=$(dirname "$(readlink -f "$0")")

与路径中的符号链接和空格一起使用.有关dirnamereadlink的信息,请参见手册页.

编辑:

从评论轨道看来它似乎不适用于Mac OS.我不知道为什么会这样.有什么建议?

  • 使用您的解决方案,调用`./ script.sh`这样的脚本显示`.`而不是完整的目录路径
  • MacOS上的readlink没有-f选项.请改用`stat`.但是,如果你是'这个',那么它会显示`.`.
  • 你需要从 Homebrew 安装 `coreutils` 并使用 `greadlink` 在 MacOS 上获得 `-f` 选项,因为它是*BSD 而不是 Linux。

pwd可用于查找当前工作目录,并dirname查找特定文件的目录(运行的命令,是$0,因此dirname $0应该为您提供当前脚本的目录).

但是,dirname准确地给出文件名的目录部分,其中很可能相对于当前工作目录.如果您的脚本由于某种原因需要更改目录,那么输出dirname将变得毫无意义.

我建议如下:

#!/bin/bash
reldir=`dirname $0`
cd $reldir
directory=`pwd`
echo "Directory is $directory"

这样,你得到一个绝对的,而不是相对的目录.

由于脚本将在单独的bash实例中运行,因此之后无需恢复工作目录,但如果由于某种原因确实想要更改脚本,则可以pwd在之前轻松地将值赋值给变量更改目录,以备将来使用.

虽然只是

cd `dirname $0`

解决了问题中的具体情况,我发现通常有更多有用的绝对路径.

  • 你可以像这样在一行中完成所有这些:DIRECTORY = $(cd`dirname $ 0` && pwd)

我不认为这和其他人一样容易.pwd不起作用,因为当前目录不一定是脚本的目录.$ 0并不总是有信息.考虑以下三种调用脚本的方法.

./script
/usr/bin/script
script

在第一和第三种方式中,$ 0没有完整的路径信息.在第二和第三,pwd不起作用.以第三种方式获取目录的唯一方法是遍历路径并找到具有正确匹配的文件.基本上代码必须重做操作系统的功能.

执行所要求的一种方法是仅对/ usr/share目录中的数据进行硬编码,并通过完整路径引用它.无论如何数据都不在/ usr/bin目录中,所以这可能是要做的事情.

  • 如果您打算反驳他的评论,请证明脚本可以通过代码示例访问存储的位置.

SCRIPT_DIR=$( cd ${0%/*} && pwd -P )
  • 正如前面的许多答案详细解释的那样,根据脚本的调用方式,不能保证$ 0和pwd都具有正确的信息。

我厌倦了一遍又一遍地到这个页面复制粘贴在接受的答案中的单行.问题在于它不易理解和记忆.

这是一个易于记忆的脚本:

DIR=$(dirname "${BASH_SOURCE[0]}")  # get the directory name
DIR=$(realpath "${DIR}")    # resolve its full path if need be
  • 或者,更晦涩的是,在一行上:`DIR=$(realpath "$(dirname "${BASH_SOURCE[0]}")")`
  • Why isn't this the accepted answer? Is there any difference using `realpath` from resolving "manually" with a loop of `readlink`? Even the `readlink` man page says `Note realpath(1) is the preferred command to use for canonicalization functionality.`

这将获得Mac OS X 10.6.6上的当前工作目录:

DIR=$(cd "$(dirname "$0")"; pwd)

$(dirname "$(readlink -f "$BASH_SOURCE")")

这是特定于Linux的,但您可以使用:

SELF=$(readlink /proc/$$/fd/255)
  • 交互式shell!=脚本.无论如何`realpath $ {BASH_SOURCE [0]};`似乎是最好的方式.

这是符合POSIX标准的单线程:

SCRIPT_PATH=`dirname "$0"`; SCRIPT_PATH=`eval "cd "$SCRIPT_PATH" && pwd"`
# test
echo $SCRIPT_PATH
  • 当我自己或使用sudo运行脚本时,我获得了成功,但在调用source ./script.sh时却没有

我尝试了其中的每一个,但没有一个工作.一个非常接近,但有一个小小的虫子,打破了它; 他们忘了用引号包住路径.

还有很多人认为你是从shell运行脚本所以忘记当你打开一个新的脚本它默认你的家.

试试这个目录的大小:

/ var /没有人/思考/关于空间存在/在目录/名称/这里是你的file.text

无论您运行的方式和位置如何,这都是正确的.

#!/bin/bash
echo "pwd: `pwd`"
echo "$0: $0"
echo "basename: `basename "$0"`"
echo "dirname: `dirname "$0"`"

因此,为了使它真正有用,这里是如何更改到正在运行的脚本的目录:

cd "`dirname "$0"`"

希望有所帮助

  • 如果脚本来自其他脚本,则不起作用.

这是一个简单,正确的方法:

actual_path=$(readlink -f "${BASH_SOURCE[0]}")
script_dir=$(dirname "$actual_path")

说明:


我会用这样的东西:

# retrieve the full pathname of the called script
scriptPath=$(which $0)
# check whether the path is a link or not
if [ -L $scriptPath ]; then
# it is a link then retrieve the target path and get the directory name
sourceDir=$(dirname $(readlink -f $scriptPath))
else
# otherwise just get the directory name of the script path
sourceDir=$(dirname $scriptPath)
fi

对e-satisf和3bcdnlklvc04a解决方案的略微修改在他们的回答中指出

SCRIPT_DIR=''
pushd "$(dirname "$(readlink -f "$BASH_SOURCE")")" > /dev/null && {
SCRIPT_DIR="$PWD"
popd > /dev/null
}

这应该仍然适用于他们列出的所有情况.

编辑:在推送失败后防止弹出,感谢konsolebox


#!/bin/sh
PRG="$0"
# need this for relative symlinks
while [ -h "$PRG" ] ; do
PRG=`readlink "$PRG"`
done
scriptdir=`dirname "$PRG"`

$ _值得一提,作为0美元的替代品.如果您从bash运行脚本,则接受的答案可以缩短为:

DIR="$( dirname "$_" )"

请注意,这必须是脚本中的第一个语句.

  • 如果你是'source`或`.`脚本,它就会中断.在这些情况下,`$ _`将包含你在`.`之前运行的最后一个命令的最后一个参数.`$ BASH_SOURCE`每次都有效.

我已经比较了给出的许多答案,并提出了一些更紧凑的解决方案.这些似乎可以处理由您最喜欢的组合产生的所有疯狂边缘情况:

  • 绝对路径或相对路径
  • 文件和目录软链接
  • 调用为script,bash script,bash -c script,source script,或者. script
  • 目录和/或文件名中的空格,制表符,换行符,unicode等
  • 文件名以连字符开头

如果您从Linux运行,似乎使用proc句柄是找到当前正在运行的脚本的完全解析源的最佳解决方案(在交互式会话中,链接指向相应的/dev/pts/X):

resolved="$(readlink /proc/$$/fd/255 && echo X)" && resolved="${resolved%$'nX'}"

这有一点点丑陋,但修复紧凑,易于理解.我们不仅仅使用bash原语,但我很好,因为readlink大大简化了任务.在echo X增加了一个X,这样的文件名的任何尾随空白不被吃掉,而参数替代的变量字符串的结尾${VAR%X}在该行的末尾摆脱的X.因为readlink添加了自己的换行符(如果不是我们之前的技巧,通常会在命令替换中使用它),我们也必须摆脱它.这是使用$''引用方案最容易实现的,它允许我们使用转义序列n来表示换行符(这也是你如何轻松地制作狡猾的命名目录和文件).

以上内容应该包括您在Linux上查找当前正在运行的脚本的需求,但如果您没有可供使用的proc文件系统,或者您正试图找到其他文件的完全解析路径,那么也许您会找到以下代码很有帮助.这只是对上述单线的略微修改.如果你正在玩奇怪的目录/文件名,用两者检查输出ls并提供readlink信息,ls输出"简化"路径,替换?换行之类的东西.

absolute_path=$(readlink -e -- "${BASH_SOURCE[0]}" && echo x) && absolute_path=${absolute_path%?x}
dir=$(dirname -- "$absolute_path" && echo x) && dir=${dir%?x}
file=$(basename -- "$absolute_path" && echo x) && file=${file%?x}
ls -l -- "$dir/$file"
printf '$absolute_path: "%s"n' "$absolute_path"

最简单,最优雅的方法是:

#!/bin/bash
DIRECTORY=$(cd `dirname $0` && pwd)
echo $DIRECTORY

这将适用于所有平台,并且超级干净。

可以在这里找到更多详细信息:https : //www.electrictoolbox.com/bash-script-directory/


尝试使用:

real=$(realpath $(dirname $0))
  • realpath不是标准实用程序.
  • 在 Linux 上,realpath 是一个标准实用程序(GNU coreutils 包的一部分),但它不是内置的 bash(即 bash 本身提供的功能)。如果您正在运行 Linux,此方法可能会起作用,尽管我会将“$0”替换为“${BASH_SOURCE[0]}”,以便此方法可以在任何地方使用,包括在函数中。
  • 此答案中的操作顺序是错误的.你需要_first_解析符号链接,_then_做`dirname`,因为`$ 0`的最后一部分可能是一个符号链接,它指向一个与符号链接本身不在同一目录下的文件.本回答中描述的解决方案只获取它存储的符号链接所在目录的路径,而不是目标的目录.此外,该解决方案缺少引用.如果路径包含特殊字符,则无效.
  • `dir =“ $(realpath” $(dirname“ $ {BASH_SOURCE [0]}”)“)”)``

对于具有GNU coreutils readlink(例如linux)的系统:

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

包含脚本文件名BASH_SOURCE时无需使用$0.

  • 除非脚本的来源是。或“源”,在这种情况下,它将仍然是源于它的脚本,或者,如果从命令行,则是“ -bash”(tty登录)或“ bash”(通过“ bash -l”调用)或“ / bin / bash'(作为交互式非登录shell调用)

如何获取正在运行的任何脚本的完整文件路径完整目录基本文件名

在许多情况下,您需要获取的只是您刚刚调用的脚本的完整路径。这可以使用 轻松完成realpath。请注意,这realpathGNU coreutils 的一部分。如果你还没有安装它(它在 Ubuntu 上是默认的),你可以用sudo apt update && sudo apt install coreutils.

get_script_path.sh

#!/bin/bash
FULL_PATH_TO_SCRIPT="$(realpath "$0")"
# You can then also get the full path to the directory, and the base
# filename, like this:
SCRIPT_DIRECTORY="$(dirname "$FULL_PATH_TO_SCRIPT")"
SCRIPT_FILENAME="$(basename "$FULL_PATH_TO_SCRIPT")"
# Now print it all out
echo "FULL_PATH_TO_SCRIPT = "$FULL_PATH_TO_SCRIPT""
echo "SCRIPT_DIRECTORY    = "$SCRIPT_DIRECTORY""
echo "SCRIPT_FILENAME     = "$SCRIPT_FILENAME""

示例输出:

~/GS/dev/eRCaGuy_hello_world/bash$ ./get_script_path.sh
FULL_PATH_TO_SCRIPT = "/home/gabriel/GS/dev/eRCaGuy_hello_world/bash/get_script_path.sh"
SCRIPT_DIRECTORY    = "/home/gabriel/GS/dev/eRCaGuy_hello_world/bash"
SCRIPT_FILENAME     = "get_script_path.sh"

请注意,realpath还成功地走下符号链接来确定并指向它们的目标,而不是指向符号链接。

上面的代码现在是我的eRCaGuy_hello_world 存储库的一部分,位于此文件中:bash/get_script_path.sh。

参考:

  1. 如何检索给定相对的绝对路径

尝试以下交叉兼容的解决方案:

CWD="$(cd -P -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"

as realpathreadlink命令并不总是可用(取决于操作系统),并且${BASH_SOURCE[0]}仅在bash shell中可用.

或者,您可以在bash中尝试以下功能:

realpath () {
[[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}"
}

此函数需要1个参数.如果参数已经是绝对路径,则按原样打印,否则打印$0变量+文件名参数(不带source前缀).

有关:

  • 如何将当前工作目录设置为脚本目录?
  • 使用OSX的Bash脚本绝对路径
  • bash脚本可靠的方式来获取自己的完整路径?

所以...我相信我有这个.晚到派对,但我想有些人会欣赏它在这里是他们遇到这个线程.评论应该解释.

#!/bin/sh # dash bash ksh # !zsh (issues). G. Nixon, 12/2013. Public domain.
## 'linkread' or 'fullpath' or (you choose) is a little tool to recursively
## dereference symbolic links (ala 'readlink') until the originating file
## is found. This is effectively the same function provided in stdlib.h as
## 'realpath' and on the command line in GNU 'readlink -f'.
## Neither of these tools, however, are particularly accessible on the many
## systems that do not have the GNU implementation of readlink, nor ship
## with a system compiler (not to mention the requisite knowledge of C).
## This script is written with portability and (to the extent possible, speed)
## in mind, hence the use of printf for echo and case statements where they
## can be substituded for test, though I've had to scale back a bit on that.
## It is (to the best of my knowledge) written in standard POSIX shell, and
## has been tested with bash-as-bin-sh, dash, and ksh93. zsh seems to have
## issues with it, though I'm not sure why; so probably best to avoid for now.
## Particularly useful (in fact, the reason I wrote this) is the fact that
## it can be used within a shell script to find the path of the script itself.
## (I am sure the shell knows this already; but most likely for the sake of
## security it is not made readily available. The implementation of "$0"
## specificies that the $0 must be the location of **last** symbolic link in
## a chain, or wherever it resides in the path.) This can be used for some
## ...interesting things, like self-duplicating and self-modifiying scripts.
## Currently supported are three errors: whether the file specified exists
## (ala ENOENT), whether its target exists/is accessible; and the special
## case of when a sybolic link references itself "foo -> foo": a common error
## for beginners, since 'ln' does not produce an error if the order of link
## and target are reversed on the command line. (See POSIX signal ELOOP.)
## It would probably be rather simple to write to use this as a basis for
## a pure shell implementation of the 'symlinks' util included with Linux.
## As an aside, the amount of code below **completely** belies the amount
## effort it took to get this right -- but I guess that's coding for you.
##===-------------------------------------------------------------------===##
for argv; do :; done # Last parameter on command line, for options parsing.
## Error messages. Use functions so that we can sub in when the error occurs.
recurses(){ printf "Self-referential:nt$argv ->nt$argvn" ;}
dangling(){ printf "Broken symlink:nt$argv ->nt"$(readlink "$argv")"n" ;}
errnoent(){ printf "No such file: "$@"n" ;} # Borrow a horrible signal name.
# Probably best not to install as 'pathfull', if you can avoid it.
pathfull(){ cd "$(dirname "$@")"; link="$(readlink "$(basename "$@")")"
## 'test and 'ls' report different status for bad symlinks, so we use this.
if [ ! -e "$@" ]; then if $(ls -d "$@" 2>/dev/null) 2>/dev/null;  then
errnoent 1>&2; exit 1; elif [ ! -e "$@" -a "$link" = "$@" ];   then
recurses 1>&2; exit 1; elif [ ! -e "$@" ] && [ ! -z "$link" ]; then
dangling 1>&2; exit 1; fi
fi
## Not a link, but there might be one in the path, so 'cd' and 'pwd'.
if [ -z "$link" ]; then if [ "$(dirname "$@" | cut -c1)" = '/' ]; then
printf "$@n"; exit 0; else printf "$(pwd)/$(basename "$@")n"; fi; exit 0
fi
## Walk the symlinks back to the origin. Calls itself recursivly as needed.
while [ "$link" ]; do
cd "$(dirname "$link")"; newlink="$(readlink "$(basename "$link")")"
case "$newlink" in
"$link") dangling 1>&2 && exit 1                                       ;;
'') printf "$(pwd)/$(basename "$link")n"; exit 0                 ;;
*) link="$newlink" && pathfull "$link"                           ;;
esac
done
printf "$(pwd)/$(basename "$newlink")n"
}
## Demo. Install somewhere deep in the filesystem, then symlink somewhere
## else, symlink again (maybe with a different name) elsewhere, and link
## back into the directory you started in (or something.) The absolute path
## of the script will always be reported in the usage, along with "$0".
if [ -z "$argv" ]; then scriptname="$(pathfull "$0")"
# Yay ANSI l33t codes! Fancy.
printf "n33[3mfrom/as: 33[4m$033[0mnn33[1mUSAGE:33[0m   "
printf "33[4m$scriptname33[24m [ link | file | dir ]nn         "
printf "Recursive readlink for the authoritative file, symlink after "
printf "symlink.nnn         33[4m$scriptname33[24mnn        "
printf " From within an invocation of a script, locate the script's "
printf "own filen         (no matter where it has been linked or "
printf "from where it is being called).nn"
else pathfull "$@"
fi

总结许多答案:

    Script: "/tmp/src dir/test.sh"
Calling folder: "/tmp/src dir/other"

使用的命令

    echo Script-Dir : `dirname "$(realpath $0)"`
echo Script-Dir : $( cd ${0%/*} && pwd -P )
echo Script-Dir : $(dirname "$(readlink -f "$0")")
echo
echo Script-Name : `basename "$(realpath $0)"`
echo Script-Name : `basename $0`
echo
echo Script-Dir-Relative : `dirname "$BASH_SOURCE"`
echo Script-Dir-Relative : `dirname $0`
echo
echo Calling-Dir : `pwd`

输出:

     Script-Dir : /tmp/src dir
Script-Dir : /tmp/src dir
Script-Dir : /tmp/src dir
Script-Name : test.sh
Script-Name : test.sh
Script-Dir-Relative : ..
Script-Dir-Relative : ..
Calling-Dir : /tmp/src dir/other

参见
https://pastebin.com/J8KjxrPF


这适用于bash-3.2:

path="$( dirname "$( which "$0" )" )"

以下是其用法示例:

假设你有一个〜/ bin目录,它在$ PATH中.您在此目录中有脚本A. 的剧本〜/斌/ lib中/ B.您知道包含的脚本相对于原始脚本(子目录)的位置,而不是它相对于用户当前目录的位置.

这可以通过以下方法解决(在A中):

source "$( dirname "$( which "$0" )" )/lib/B"

无论用户在哪里或者如何调用脚本都无关紧要,这将始终有效.

  • 关于`which`的观点是非常值得商榷的.`type`,`hash`和其他内置函数在bash中做得更好.`which`有点便携,虽然它真的不像在tcsh之类的其他shell中使用的那个`,它把它作为内置.

嗯,如果在路径basename&dirname中只是不打算切断它并且走路很难(如果父节点没有导出PATH怎么办!).但是,shell必须有一个打开它的脚本句柄,而在bash中句柄是#255.

SELF=`readlink /proc/$$/fd/255`

适合我.


您只需将脚本名称 ( $0) 与realpath和/或组合起来即可dirname。它适用于 Bash 和 Shell。

#!/usr/bin/env bash
RELATIVE_PATH="${0}"
RELATIVE_DIR_PATH="$(dirname "${0}")"
FULL_DIR_PATH="$(realpath "${0}" | xargs dirname)"
FULL_PATH="$(realpath "${0}")"
echo "RELATIVE_PATH->${RELATIVE_PATH}<-"
echo "RELATIVE_DIR_PATH->${RELATIVE_DIR_PATH}<-"
echo "FULL_DIR_PATH->${FULL_DIR_PATH}<-"
echo "FULL_PATH->${FULL_PATH}<-"

输出将是这样的:

# RELATIVE_PATH->./bin/startup.sh<-
# RELATIVE_DIR_PATH->./bin<-
# FULL_DIR_PATH->/opt/my_app/bin<-
# FULL_PATH->/opt/my_app/bin/startup.sh<-

4.4. 特殊变量类型

一个例子:LozanoMatheus/get_script_paths.sh


我认为最好的紧凑型解决方案是:

"$( cd "$( echo "${BASH_SOURCE[0]%/*}" )"; pwd )"

除了Bash之外,没有任何依赖.使用dirname,readlinkbasename最终会导致兼容性问题,因此如果可能的话,最好避免使用它们.

  • 您可能应该在其中添加斜杠:`“ $(cd” $(echo“ $ {BASH_SOURCE [0]%/ *} /”)“; pwd)”`。如果没有,您将在根目录上遇到问题。另外,为什么还要使用echo?

这些都不适用于Finder在OS X中启动的bash脚本 - 我最终使用:

SCRIPT_LOC="`ps -p $$ | sed /PID/d | sed s:.*/Network/:/Network/: |
sed s:.*/Volumes/:/Volumes/:`"

不漂亮,但它完成了工作.


使用readlink的组合来规范化名称(如果它是符号链接,则可以将其跟踪回其源)和dirname以提取目录名称:

script="`readlink -f "${BASH_SOURCE[0]}"`"
dir="`dirname "$script"`"

当其他答案在这里没有时,这对我有用:

thisScriptPath=`realpath $0`
thisDirPath=`dirname $thisScriptPath`
echo $thisDirPath
  • 请注意,没有可求助于回声的人。只需调用“ dirname”即可打印目录名称。而且,在这种情况下,调用回波而未正确引用该变量将可能产生不同的输出,因为它将压缩空格。总的来说,更清洁的方法是简单地写出`dirname“ $(realpath” $ 0“)”`

顶级响应并非在所有情况下都有效...

由于在通过“sh my_script.sh”调用 shell 脚本时,我在一些非常新的和不太新的安装的Ubuntu 16.04(Xenial Xerus)系统上使用包含的“cd”方法遇到了 BASH_SOURCE 问题,因此我尝试了一些方法不同的是,到目前为止,对于我的目的来说似乎运行得相当顺利。该方法在脚本中更加紧凑,并且更不那么神秘。

这种替代方法使用coreutils 包中的外部应用程序“ realpath ”和“ dirname ”。(好吧,没有人喜欢调用辅助进程的开销——但是当看到用于解析真实对象的多行脚本时,它不会那么糟糕,无论是在单个二进制使用中解决它。)

因此,让我们看一下用于查询特定文件的真实绝对路径的描述任务的替代解决方案的一个示例:

PATH_TO_SCRIPT=`realpath -s $0`
PATH_TO_SCRIPT_DIR=`dirname $PATH_TO_SCRIPT`

但最好你应该使用这个进化版本来支持使用带有空格的路径(或者甚至一些其他特殊字符):

PATH_TO_SCRIPT=`realpath -s "$0"`
PATH_TO_SCRIPT_DIR=`dirname "$PATH_TO_SCRIPT"`

实际上,如果您不需要 SCRIPT 变量的值,那么您可能能够将这两个行合并为一行。但你为什么真的要为此付出努力呢?


以上是从脚本本身获取Bash脚本的源目录的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>