基本上我需要运行与shell脚本文件位置相关的路径脚本,我如何将当前目录更改为脚本文件所在的相同目录?
当前回答
介绍
这个答案纠正了这个帖子(由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
其他回答
这个问题的最佳答案是: 从内部获取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'"
cd $(dirname $(readlink -f $0))
So many answers, all plausible, each with pro's and con's & slightly differeing objectives (which should probably be stated for each). Here's another solution that meets a primary objective of both being clear and working across all systems, on all bash (no assumptions about bash versions, or readlink or pwd options), and reasonably does what you'd expect to happen (eg, resolving symlinks is an interesting problem, but isn't usually what you actually want), handle edge cases like spaces in paths, etc., ignores any errors and uses a sane default if there are any issues.
每个组件都存储在一个单独的变量中,您可以单独使用:
# 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)"
这一行代码告诉shell脚本在哪里,与您是否运行它或是否获取它无关。此外,它还会解析所涉及的任何符号链接,如果是这样的话:
dir=$(dirname $(test -L "$BASH_SOURCE" && readlink -f "$BASH_SOURCE" || echo "$BASH_SOURCE"))
顺便说一下,我认为您正在使用/bin/bash.
如果您正在使用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}"