基本上我需要使用与shell脚本文件位置相关的路径来运行脚本,如何将当前目录更改为与脚本文件所在目录相同?
您可以使用以下命令将当前工作目录更改为与脚本文件所在目录相同:``` cd "$(dirname "$0")" ```
其中 `$0` 是脚本文件的名称,`$(dirname "$0")` 获取该文件所在的目录,并使用 `cd` 命令将当前工作目录切换到那个目录。
基本上我需要使用与shell脚本文件位置相关的路径来运行脚本,如何将当前目录更改为与脚本文件所在目录相同?
您可以使用以下命令将当前工作目录更改为与脚本文件所在目录相同:BASE_DIR="$(cd "$(dirname "$0")"; pwd)";
echo "BASE_DIR => $BASE_DIR"
介绍
这篇答案纠正了这个帖子中最高票的但非常错误的答案(由TheMarko撰写):
#!/usr/bin/env bash
BASEDIR=$(dirname "$0")
echo "$BASEDIR"
为什么只使用dirname "$0"不能正常工作?
dirname $0只有在用户以非常特定的方式启动脚本时才能起作用。我发现了几种情况,这个答案会失败并使脚本崩溃。
首先,让我们了解一下这个答案是如何工作的。他通过执行以下命令来获取脚本目录:
dirname "$0"
$0 代表调用脚本的第一部分命令(它基本上是输入的命令而没有参数:
/some/path/./script argument1 argument2
$0 = "/some/path/./script"
dirname 基本上是在字符串中找到最后一个 / 并将其截断。因此,如果您执行以下操作:
dirname /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
. #or BASEDIR=".", again this will crash your script
使用:
sh script
没有输入完整路径也会给出BASEDIR="。"
使用相对目录:
../some/path/./script
给出$0的目录名:
../some/path/.
如果您在 /some 目录中,并以这种方式调用脚本(请注意开头没有 /,再次强调这是相对路径):
path/./script.sh
path/.
而 ./path/./script(另一种相对路径形式)则会返回:
./path/.
$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
PATH
,那么你只需cd
到文件夹并获取pwd
。但我想通常需要获取路径的原因是为了能够相对于当前路径调用其他脚本,在这种情况下,我无法看出任何这些情况会导致任何问题。 - Timo这个一行命令可以告诉你脚本所在的位置,无论你运行它还是引用它。而且,如果涉及符号链接,它还会解析它们:
dir=$(dirname $(test -L "$BASH_SOURCE" && readlink -f "$BASH_SOURCE" || echo "$BASH_SOURCE"))
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")")"
dir=$(dirname $(which $0 2>/dev/null || echo $0))
。 - anton_rhdir=$(dirname $(which $0 2>/dev/null || echo $0))
。 - undefined# 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)"
我发现最稳健的方法之一是
#!/bin/sh
relative_dir=`perl -e 'use Cwd "realpath";$pwd = realpath(shift); $pwd =~ s/\/[^\/]*$//; print $pwd' $0`
cd $relative_dir
可以使用符号链接,并且无论同事们选择哪种shell类型,都能正常工作。
使用tcsh,您可以使用:h
变量修饰符来检索路径。
一个注意点是,如果脚本作为tcsh myscript.csh
执行,则只会获取脚本名称。解决方法是按下面所示验证路径。
#!/bin/tcsh
set SCRIPT_PATH = $0:h
if ( $SCRIPT_PATH == $0 ) then
set SCRIPT_PATH = "."
endif
$SCRIPT_PATH/compile.csh > $SCRIPT_PATH/results.txt
有关变量修饰符的更多信息可以在https://learnxinyminutes.com/docs/tcsh/找到。
应该就可以了:
echo `pwd`/`dirname $0`
根据调用方式和当前工作目录,它可能看起来很丑,但应该能带你到你需要去的地方(或者如果你在意它的外观,你可以调整字符串)。
\
pwd`/`dirname $0``,但可能仍然会在符号链接上失败。 - Andreas Covidiot