如何获取其中的目录路径的Bash脚本位于,里面那个脚本?
例如,假设我想使用Bash脚本作为另一个应用程序的启动器.我想将工作目录更改为Bash脚本所在的目录,因此我可以对该目录中的文件进行操作,如下所示:
$ ./application
dog*_*ane 6204
#!/bin/bash
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
Run Code Online (Sandbox Code Playgroud)
是一个有用的单行程序,它将为您提供脚本的完整目录名称,无论它在何处被调用.
只要用于查找脚本的路径的最后一个组件不是符号链接(目录链接正常),它就会起作用.如果您还想解决脚本本身的任何链接,则需要一个多行解决方案:
#!/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 )"
Run Code Online (Sandbox Code Playgroud)
这最后一个将与别名的任意组合工作,source,bash -c,符号链接等.
注意:如果您cd在运行此代码段之前访问其他目录,结果可能不正确!
另外,如果用户巧妙地重写了cd以将输出重定向到stderr(包括转义序列,例如在Mac上调用时),请注意$CDPATH陷阱和stderr输出副作用update_terminal_cwd >&2.>/dev/null 2>&1在cd命令结束时添加将兼顾两种可能性.
要了解它的工作原理,请尝试运行此更详细的表单:
#!/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'"
Run Code Online (Sandbox Code Playgroud)
它将打印如下:
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'
Run Code Online (Sandbox Code Playgroud)
mat*_*t b 824
用途dirname "$0":
#!/bin/bash
echo "The script you are running has basename `basename "$0"`, dirname `dirname "$0"`"
echo "The present working directory is `pwd`"
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
pha*_*lat 464
dirname命令是最基本的,只需解析直到$ 0(脚本名称)变量的文件名的路径:
dirname "$0"
Run Code Online (Sandbox Code Playgroud)
但是,正如matt b指出的那样,返回的路径会有所不同,具体取决于脚本的调用方式.pwd没有完成这项工作,因为它只告诉你当前目录是什么,而不是脚本所在的目录.另外,如果执行了一个脚本的符号链接,你将获得一个(可能是相对的)路径到链接所在的位置,而不是实际的脚本.
其他一些人提到了readlink命令,但最简单的是,您可以使用:
dirname "$(readlink -f "$0")"
Run Code Online (Sandbox Code Playgroud)
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))"
Run Code Online (Sandbox Code Playgroud)
使用相对路径在我的主目录中运行此脚本:
>>>$ ./whatdir.sh
pwd: /Users/phatblat
$0: ./whatdir.sh
basename: whatdir.sh
dirname: .
dirname/readlink: /Users/phatblat
Run Code Online (Sandbox Code Playgroud)
同样,但使用脚本的完整路径:
>>>$ /Users/phatblat/whatdir.sh
pwd: /Users/phatblat
$0: /Users/phatblat/whatdir.sh
basename: whatdir.sh
dirname: /Users/phatblat
dirname/readlink: /Users/phatblat
Run Code Online (Sandbox Code Playgroud)
现在改变目录:
>>>$ cd /tmp
>>>$ ~/whatdir.sh
pwd: /tmp
$0: /Users/phatblat/whatdir.sh
basename: whatdir.sh
dirname: /Users/phatblat
dirname/readlink: /Users/phatblat
Run Code Online (Sandbox Code Playgroud)
最后使用符号链接来执行脚本:
>>>$ ln -s ~/whatdir.sh whatdirlink.sh
>>>$ ./whatdirlink.sh
pwd: /tmp
$0: ./whatdirlink.sh
basename: whatdirlink.sh
dirname: .
dirname/readlink: /Users/phatblat
Run Code Online (Sandbox Code Playgroud)
小智 179
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
Run Code Online (Sandbox Code Playgroud)
适用于所有版本,包括
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
Run Code Online (Sandbox Code Playgroud)
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
Fab*_*ien 105
简短回答:
`dirname $0`
Run Code Online (Sandbox Code Playgroud)
或(最好):
$(dirname "$0")
Run Code Online (Sandbox Code Playgroud)
Mr *_*ark 100
您可以使用$ BASH_SOURCE
#!/bin/bash
scriptdir=`dirname "$BASH_SOURCE"`
Run Code Online (Sandbox Code Playgroud)
请注意,您需要使用#!/ bin/bash而不是#!/ bin/sh,因为它是一个bash扩展名
Sim*_*gét 69
这应该这样做:
DIR=$(dirname "$(readlink -f "$0")")
Run Code Online (Sandbox Code Playgroud)
与路径中的符号链接和空格一起使用.有关dirname和readlink的信息,请参见手册页.
编辑:
从评论轨道看来它似乎不适用于Mac OS.我不知道为什么会这样.有什么建议?
Spo*_*ser 60
pwd可用于查找当前工作目录,并dirname查找特定文件的目录(运行的命令,是$0,因此dirname $0应该为您提供当前脚本的目录).
但是,dirname准确地给出文件名的目录部分,其中很可能相对于当前工作目录.如果您的脚本由于某种原因需要更改目录,那么输出dirname将变得毫无意义.
我建议如下:
#!/bin/bash
reldir=`dirname $0`
cd $reldir
directory=`pwd`
echo "Directory is $directory"
Run Code Online (Sandbox Code Playgroud)
这样,你得到一个绝对的,而不是相对的目录.
由于脚本将在单独的bash实例中运行,因此之后无需恢复工作目录,但如果由于某种原因确实想要更改脚本,则可以pwd在之前轻松地将值赋值给变量更改目录,以备将来使用.
虽然只是
cd `dirname $0`
Run Code Online (Sandbox Code Playgroud)
解决了问题中的具体情况,我发现通常有更多有用的绝对路径.
小智 36
我不认为这和其他人一样容易.pwd不起作用,因为当前目录不一定是脚本的目录.$ 0并不总是有信息.考虑以下三种调用脚本的方法.
./script
/usr/bin/script
script
Run Code Online (Sandbox Code Playgroud)
在第一和第三种方式中,$ 0没有完整的路径信息.在第二和第三,pwd不起作用.以第三种方式获取目录的唯一方法是遍历路径并找到具有正确匹配的文件.基本上代码必须重做操作系统的功能.
执行所要求的一种方法是仅对/ usr/share目录中的数据进行硬编码,并通过完整路径引用它.无论如何数据都不在/ usr/bin目录中,所以这可能是要做的事情.
P M*_*P M 34
SCRIPT_DIR=$( cd ${0%/*} && pwd -P )
Run Code Online (Sandbox Code Playgroud)
Tha*_*wda 33
我厌倦了一遍又一遍地到这个页面复制粘贴在接受的答案中的单行.问题在于它不易理解和记忆.
这是一个易于记忆的脚本:
DIR=$(dirname "${BASH_SOURCE[0]}") # get the directory name
DIR=$(realpath "${DIR}") # resolve its full path if need be
Run Code Online (Sandbox Code Playgroud)
小智 32
这将获得Mac OS X 10.6.6上的当前工作目录:
DIR=$(cd "$(dirname "$0")"; pwd)
Run Code Online (Sandbox Code Playgroud)
Ste*_*ker 27
这是特定于Linux的,但您可以使用:
SELF=$(readlink /proc/$$/fd/255)
Run Code Online (Sandbox Code Playgroud)
小智 22
这是符合POSIX标准的单线程:
SCRIPT_PATH=`dirname "$0"`; SCRIPT_PATH=`eval "cd \"$SCRIPT_PATH\" && pwd"`
# test
echo $SCRIPT_PATH
Run Code Online (Sandbox Code Playgroud)
小智 17
我尝试了其中的每一个,但没有一个工作.一个非常接近,但有一个小小的虫子,打破了它; 他们忘了用引号包住路径.
还有很多人认为你是从shell运行脚本所以忘记当你打开一个新的脚本它默认你的家.
试试这个目录的大小:
/ var /没有人/思考/关于空间存在/在目录/名称/这里是你的file.text
无论您运行的方式和位置如何,这都是正确的.
#!/bin/bash
echo "pwd: `pwd`"
echo "\$0: $0"
echo "basename: `basename "$0"`"
echo "dirname: `dirname "$0"`"
Run Code Online (Sandbox Code Playgroud)
因此,为了使它真正有用,这里是如何更改到正在运行的脚本的目录:
cd "`dirname "$0"`"
Run Code Online (Sandbox Code Playgroud)
希望有所帮助
Jam*_* Ko 17
这是一个简单,正确的方法:
actual_path=$(readlink -f "${BASH_SOURCE[0]}")
script_dir=$(dirname "$actual_path")
Run Code Online (Sandbox Code Playgroud)
说明:
${BASH_SOURCE[0]} - 脚本的完整路径.即使在编写脚本时,这个值也是正确的,例如source <(echo 'echo $0')打印bash,而替换它将${BASH_SOURCE[0]}打印脚本的完整路径.(当然,这假设您可以依赖Bash.)
readlink -f - 递归地解析指定路径中的任何符号链接.这是GNU扩展,在(例如)BSD系统上不可用.如果您正在运行Mac,则可以使用Homebrew来安装GNU coreutils并将其取代greadlink -f.
当然,dirname获取路径的父目录.
Nic*_*las 15
我会用这样的东西:
# 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
Run Code Online (Sandbox Code Playgroud)
Fuw*_*jax 14
对e-satisf和3bcdnlklvc04a解决方案的略微修改在他们的回答中指出
SCRIPT_DIR=''
pushd "$(dirname "$(readlink -f "$BASH_SOURCE")")" > /dev/null && {
SCRIPT_DIR="$PWD"
popd > /dev/null
}
Run Code Online (Sandbox Code Playgroud)
这应该仍然适用于他们列出的所有情况.
编辑:在推送失败后防止弹出,感谢konsolebox
小智 13
#!/bin/sh
PRG="$0"
# need this for relative symlinks
while [ -h "$PRG" ] ; do
PRG=`readlink "$PRG"`
done
scriptdir=`dirname "$PRG"`
Run Code Online (Sandbox Code Playgroud)
woo*_*hoo 13
令人难以置信的是,无论您如何调用脚本,都可以如此简单:
#!/bin/bash
#
the_source=$(readlink -f ${BASH_SOURCE[0]})
the_dirname=$(dirname ${the_source})
echo "the_source: ${the_source}"
echo "the_dirname: ${the_dirname}"
Run Code Online (Sandbox Code Playgroud)
从任何地方运行:
user@computer:~/Downloads/temp$ ./test.sh
Run Code Online (Sandbox Code Playgroud)
输出:
the_source: /home/user/Downloads/temp/test.sh
the_dirname: /home/user/Downloads/temp
Run Code Online (Sandbox Code Playgroud)
hur*_*lad 11
$ _值得一提,作为0美元的替代品.如果您从bash运行脚本,则接受的答案可以缩短为:
DIR="$( dirname "$_" )"
Run Code Online (Sandbox Code Playgroud)
请注意,这必须是脚本中的第一个语句.
bil*_*jmc 11
我已经比较了给出的许多答案,并提出了一些更紧凑的解决方案.这些似乎可以处理由您最喜欢的组合产生的所有疯狂边缘情况:
script,bash script,bash -c script,source script,或者. script如果您从Linux运行,似乎使用proc句柄是找到当前正在运行的脚本的完全解析源的最佳解决方案(在交互式会话中,链接指向相应的/dev/pts/X):
resolved="$(readlink /proc/$$/fd/255 && echo X)" && resolved="${resolved%$'\nX'}"
Run Code Online (Sandbox Code Playgroud)
这有一点点丑陋,但修复紧凑,易于理解.我们不仅仅使用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"
Run Code Online (Sandbox Code Playgroud)
Atu*_*tul 11
最简单,最优雅的方法是:
#!/bin/bash
DIRECTORY=$(cd `dirname $0` && pwd)
echo $DIRECTORY
Run Code Online (Sandbox Code Playgroud)
这将适用于所有平台,并且超级干净。
可以在这里找到更多详细信息:https : //www.electrictoolbox.com/bash-script-directory/
小智 10
尝试使用:
real=$(realpath $(dirname $0))
Run Code Online (Sandbox Code Playgroud)
use*_*062 10
对于具有GNU coreutils readlink(例如linux)的系统:
$(readlink -f "$(dirname "$0")")
Run Code Online (Sandbox Code Playgroud)
包含脚本文件名BASH_SOURCE时无需使用$0.
在许多情况下,您需要获取的只是您刚刚调用的脚本的完整路径。这可以使用 轻松完成realpath。请注意,这realpath是GNU 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\""
Run Code Online (Sandbox Code Playgroud)
示例输出:
Run Code Online (Sandbox Code Playgroud)~/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。
尝试以下交叉兼容的解决方案:
CWD="$(cd -P -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"
Run Code Online (Sandbox Code Playgroud)
as realpath或readlink命令并不总是可用(取决于操作系统),并且${BASH_SOURCE[0]}仅在bash shell中可用.
或者,您可以在bash中尝试以下功能:
realpath () {
[[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}"
}
Run Code Online (Sandbox Code Playgroud)
此函数需要1个参数.如果参数已经是绝对路径,则按原样打印,否则打印$0变量+文件名参数(不带source前缀).
有关:
所以...我相信我有这个.晚到派对,但我想有些人会欣赏它在这里是他们遇到这个线程.评论应该解释.
#!/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:\n\t$argv ->\n\t$argv\n" ;}
dangling(){ printf "Broken symlink:\n\t$argv ->\n\t"$(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 "\n\033[3mfrom/as: \033[4m$0\033[0m\n\n\033[1mUSAGE:\033[0m "
printf "\033[4m$scriptname\033[24m [ link | file | dir ]\n\n "
printf "Recursive readlink for the authoritative file, symlink after "
printf "symlink.\n\n\n \033[4m$scriptname\033[24m\n\n "
printf " From within an invocation of a script, locate the script's "
printf "own file\n (no matter where it has been linked or "
printf "from where it is being called).\n\n"
else pathfull "$@"
fi
Run Code Online (Sandbox Code Playgroud)
Script: "/tmp/src dir/test.sh"
Calling folder: "/tmp/src dir/other"
Run Code Online (Sandbox Code Playgroud)
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`
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
参见 https://pastebin.com/J8KjxrPF
令人恼火的是,这是我发现的唯一一行代码,当可执行脚本是符号链接时,它可以在 Linux 和 macOS 上运行:
SCRIPT_DIR=$(python -c "import os; print(os.path.dirname(os.path.realpath('${BASH_SOURCE[0]}')))")
Run Code Online (Sandbox Code Playgroud)
或者,类似地,使用 python3 pathlib 模块:
SCRIPT_DIR=$(python3 -c "from pathlib import Path; print(Path('${BASH_SOURCE[0]}').resolve().parent)")
Run Code Online (Sandbox Code Playgroud)
在 Linux 和 macOS 上进行了测试,并与本要点中的其他解决方案进行了比较:https://gist.github.com/ptc-mrucci/61772387878ed53a6c717d51a21d9371
这适用于bash-3.2:
path="$( dirname "$( which "$0" )" )"
Run Code Online (Sandbox Code Playgroud)
以下是其用法示例:
假设你有一个〜/ bin目录,它在$ PATH中.您在此目录中有脚本A. 它源的剧本〜/斌/ lib中/ B.您知道包含的脚本相对于原始脚本(子目录库)的位置,而不是它相对于用户当前目录的位置.
这可以通过以下方法解决(在A中):
source "$( dirname "$( which "$0" )" )/lib/B"
Run Code Online (Sandbox Code Playgroud)
无论用户在哪里或者如何调用脚本都无关紧要,这将始终有效.
嗯,如果在路径basename&dirname中只是不打算切断它并且走路很难(如果父节点没有导出PATH怎么办!).但是,shell必须有一个打开它的脚本句柄,而在bash中句柄是#255.
SELF=`readlink /proc/$$/fd/255`
Run Code Online (Sandbox Code Playgroud)
适合我.
您只需将脚本名称 ( $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}<-"
Run Code Online (Sandbox Code Playgroud)
输出将是这样的:
# RELATIVE_PATH->./bin/startup.sh<-
# RELATIVE_DIR_PATH->./bin<-
# FULL_DIR_PATH->/opt/my_app/bin<-
# FULL_PATH->/opt/my_app/bin/startup.sh<-
Run Code Online (Sandbox Code Playgroud)
$0 是脚本本身的名称
一个例子:LozanoMatheus/get_script_paths.sh
如果目录名称末尾有任何换行符,当前的解决方案都不起作用 - 它们将被命令替换删除。要解决此问题,您可以在命令替换中附加一个非换行符,然后仅删除该字符:
dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd && echo x)"
dir="${dir%x}"
Run Code Online (Sandbox Code Playgroud)
这可以防止两种非常常见的情况:事故和破坏。脚本不应该仅仅因为某人在某个地方做了一个mkdir $'\n'.
我认为最好的紧凑型解决方案是:
"$( cd "$( echo "${BASH_SOURCE[0]%/*}" )"; pwd )"
Run Code Online (Sandbox Code Playgroud)
除了Bash之外,没有任何依赖.使用dirname,readlink并basename最终会导致兼容性问题,因此如果可能的话,最好避免使用它们.
还有另一种变体:
SELF=$(SELF=$(dirname "$0") && bash -c "cd \"$SELF\" && pwd")
echo "$SELF"
Run Code Online (Sandbox Code Playgroud)
这也适用于 macOS,确定规范路径,并且不会更改当前目录。
这是我发现可靠的唯一方法:
SCRIPT_DIR=$(dirname $(cd "$(dirname "$BASH_SOURCE")"; pwd))
Run Code Online (Sandbox Code Playgroud)
小智 5
这些都不适用于Finder在OS X中启动的bash脚本 - 我最终使用:
SCRIPT_LOC="`ps -p $$ | sed /PID/d | sed s:.*/Network/:/Network/: |
sed s:.*/Volumes/:/Volumes/:`"
Run Code Online (Sandbox Code Playgroud)
不漂亮,但它完成了工作.
小智 5
使用readlink的组合来规范化名称(如果它是符号链接,则可以将其跟踪回其源)和dirname以提取目录名称:
script="`readlink -f "${BASH_SOURCE[0]}"`"
dir="`dirname "$script"`"
Run Code Online (Sandbox Code Playgroud)
当其他答案在这里没有时,这对我有用:
thisScriptPath=`realpath $0`
thisDirPath=`dirname $thisScriptPath`
echo $thisDirPath
Run Code Online (Sandbox Code Playgroud)
顶级响应并非在所有情况下都有效...
由于在通过“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`
Run Code Online (Sandbox Code Playgroud)
但最好你应该使用这个进化版本来支持使用带有空格的路径(或者甚至一些其他特殊字符):
PATH_TO_SCRIPT=`realpath -s "$0"`
PATH_TO_SCRIPT_DIR=`dirname "$PATH_TO_SCRIPT"`
Run Code Online (Sandbox Code Playgroud)
实际上,如果您不需要 SCRIPT 变量的值,那么您可能能够将这两个行合并为一行。但你为什么真的要为此付出努力呢?
大多数答案要么不处理通过相对路径符号链接的文件,不是单行代码,要么不处理 BSD (Mac)。解决这三个问题的解决方案是:
HERE=$(cd "$(dirname "$BASH_SOURCE")"; cd -P "$(dirname "$(readlink "$BASH_SOURCE" || echo .)")"; pwd)
Run Code Online (Sandbox Code Playgroud)
首先,cd 到bash 脚本目录的概念。然后 readlink 该文件以查看它是否是符号链接(相对或其他),如果是,则 cd 到该目录。如果没有,请 cd 到当前目录(必须保持简洁)。然后通过 回显当前目录pwd。
您可以添加--cd 和 readlink 的参数,以避免像选项这样命名的目录出现问题,但对于大多数用途,我不会打扰。
您可以在此处查看带有插图的完整说明:
https://www.binaryphile.com/bash/2020/01/12/确定-the-location-of-your-script-in-bash.html
| 归档时间: |
|
| 查看次数: |
1581792 次 |
| 最近记录: |