基本上我需要运行与shell脚本文件位置相关的路径脚本,我如何将当前目录更改为脚本文件所在的相同目录?
当前回答
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)"
其他回答
介绍
这个答案纠正了这个帖子(由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
#!/bin/bash
current_dir=$(pwd)
script_dir=$(dirname "$0")
echo $current_dir
echo $script_dir
这个脚本应该打印您所在的目录,然后是脚本所在的目录。例如,当使用/home/mez/中的脚本从/调用它时,它输出
/
/home/mez
请记住,当从命令的输出为变量赋值时,请将命令包装在$(and)中—否则将得不到所需的输出。
如果您正在使用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}"
如果你想获得实际的脚本目录(不管你是使用符号链接还是直接调用脚本),尝试:
BASEDIR=$(dirname $(realpath "$0"))
echo "$BASEDIR"
这在linux和macOS上都适用。我没看到有人提到realpath。不确定这种方法是否有任何缺点。
在macOS上,需要安装coreutils才能使用realpath。例如:brew install coreutils。
基本版:
dir=$(dirname $0)
如果脚本可以通过$PATH调用,那么:
dir=$(dirname $(which $0))
如果脚本可能像这样调用:bash script.sh,那么:
dir=$(dirname $(which $0 2>/dev/null || realpath ./$0))
如果你感到极度不安全,那么:
dir="$(dirname -- "$(which -- "$0" 2>/dev/null || realpath -- "./$0")")"