我如何找到一个Bash脚本位于该脚本内部的目录的路径?
我想用Bash脚本作为另一个应用程序的启动器,我想将工作目录更改为Bash脚本所在的目录,所以我可以在该目录中的文件上运行,如下:
$ ./application
我如何找到一个Bash脚本位于该脚本内部的目录的路径?
我想用Bash脚本作为另一个应用程序的启动器,我想将工作目录更改为Bash脚本所在的目录,所以我可以在该目录中的文件上运行,如下:
$ ./application
当前回答
下面是一个命令,在Bash或zsh下工作,以及是否单独执行或源:
[ -n "$ZSH_VERSION" ] && this_dir=$(dirname "${(%):-%x}") \
|| this_dir=$(dirname "${BASH_SOURCE[0]:-$0}")
它是如何工作的
Fallback 替代操作员:
% x=ok
% echo "${x}"
ok
% echo "${x:-fallback}"
ok
% x=
% echo "${x:-fallback}"
fallback
% y=yvalue
% echo "${x:-$y}"
yvalue
此分類上一篇: %x prompt escape code
接下来,我们将引入即时逃避代码,一个 zsh-only 功能. 在 zsh 中, %x 将扩展到文件的路径,但通常只有在进行即时行扩展时。
% cat apath/test.sh
fpath=%x
echo "${(%)fpath}"
% source apath/test.sh
apath/test.sh
% cd apath
% source test.sh
test.sh
% cat test.sh
echo "${(%):-%x}"
% source test.sh
test.sh
请注意,我们通常会把变量名称放在(%)和(%)之间,但我们留下了白色。
我们可能已经使用打印 -P %x 以获得相同的文件路径,有更少的黑客,但在我们的情况下,我们需要将其作为一个论点转到一个字符串,这将需要一个开始一个新的字符串的顶部:
% cat apath/test.sh
dirname "$(print -P %x)" # $(...) runs a command in a new process
dirname "${(%):-%x}"
% source apath/test.sh
apath
apath
显然,黑客的方式既更有效又更简短。
其他回答
#!/usr/bin/env bash
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
这是一个有用的单行,这将为您提供脚本的完整目录名称,无论它从哪里被召唤。
它将工作,只要找到脚本的路径的最后一个组成部分不是一个simlink(指南链接是OK)。如果你也想解决任何链接到脚本本身,你需要一个多线解决方案:
#!/usr/bin/env bash
SOURCE=${BASH_SOURCE[0]}
while [ -L "$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 )
最后一个将与任何结合的联盟,来源,bash -c,simlinks等工作。
注意:如果您在运行此剪辑之前将CD转到另一个目录,结果可能是错误的!
此外,请注意 $CDPATH gotchas 和 stderr 输出副作用,如果用户有明智的 overridden cd 将输出转向 stderr 而不是 (包括逃避序列,如在 Mac 上呼叫 update_terminal_cwd >&2 ) 添加 >/dev/null 2>&1 在您的 cd 命令结束时,将考虑到两种可能性。
要了解它是如何工作的,试着运行这个更垂直的形式:
#!/usr/bin/env bash
SOURCE=${BASH_SOURCE[0]}
while [ -L "$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'
查看下面的测试与奇怪的目录名称。
要将工作目录更改为Bash脚本的位置,您应该尝试这个简单的,测试和验证的Shellcheck解决方案:
#!/bin/bash --
cd "$(dirname "${0}")"/. || exit 2
测试:
$ ls
application
$ mkdir "$(printf "\1\2\3\4\5\6\7\10\11\12\13\14\15\16\17\20\21\22\23\24\25\26\27\30\31\32\33\34\35\36\37\40\41\42\43\44\45\46\47testdir" "")"
$ mv application *testdir
$ ln -s *testdir "$(printf "\1\2\3\4\5\6\7\10\11\12\13\14\15\16\17\20\21\22\23\24\25\26\27\30\31\32\33\34\35\36\37\40\41\42\43\44\45\46\47symlink" "")"
$ ls -lb
total 4
lrwxrwxrwx 1 jay stacko 46 Mar 30 20:44 \001\002\003\004\005\006\a\b\t\n\v\f\r\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037\ !"#$%&'symlink -> \001\002\003\004\005\006\a\b\t\n\v\f\r\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037\ !"#$%&'testdir
drwxr-xr-x 2 jay stacko 4096 Mar 30 20:44 \001\002\003\004\005\006\a\b\t\n\v\f\r\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037\ !"#$%&'testdir
$ *testdir/application && printf "SUCCESS\n" ""
SUCCESS
$ *symlink/application && printf "SUCCESS\n" ""
SUCCESS
下面存储了脚本的目录路径在Dir变量中。
(它也试图支持在Windows中的Cygwin下运行。
最后,它运行我的样本应用程序可执行,所有通过到这个脚本的论点使用“$@”:
#!/usr/bin/env sh
dir=$(cd "${0%[/\\]*}" > /dev/null && pwd)
if [ -d /proc/cygdrive ]; then
case "$(uname -s)" in
CYGWIN*|MINGW32*|MSYS*|MINGW*)
# We are under Windows, so translate path to Windows format.
dir=$(cygpath -m "$dir");
;;
esac
fi
# Runs the executable which is beside this script
"${dir}/my-sample-app" "$@"
没有<unk>(除了<unk>)和可以处理“陌生人”名称的形式,如那些有新闻,因为有些人会声称:
IFS= read -rd '' DIR < <([[ $BASH_SOURCE != */* ]] || cd "${BASH_SOURCE%/*}/" >&- && echo -n "$PWD")
这就是我如何在我的脚本上工作:
pathvar="$( cd "$( dirname $0 )" && pwd )"
这将告诉你从哪个目录启动器(当前脚本)正在执行。