6276

如何获取目录的路径猛击脚本位于,里面那个脚本?

我想使用Bash脚本作为另一个应用程序的启动程序。我想将工作目录更改为Bash脚本所在的目录,以便可以操作该目录中的文件,如下所示:

$ ./应用
5
  • 92
    如果存在任何解决方案,则当前的解决方案都不起作用目录名末尾的换行-它们将被替换命令删除。要解决此问题,可以在命令替换中附加一个非换行符-DIR=“$(cd”$(目录名“${BASH_SOURCE[0]}”)“&&pwd&&echo x)”-并在不替换命令的情况下删除它-DIR=“${DIR%x}”.
    – 10亿
    评论 2012年9月24日12:15
  • 97
    @jpmc26有两种非常常见的情况:事故和破坏。脚本不应该仅仅因为某人在某处执行了mkdir$“\n”.
    – 10亿
    评论 2013年3月28日8:14
  • 38
    任何让人们以这种方式破坏他们的系统的人都不应该让它去检测这样的问题。。。更不用说雇佣有能力犯这种错误的人了。在使用bash的25年里,我从未见过这种事情在任何地方发生。。。。这就是为什么我们有像perl这样的东西和像污点检查这样的实践(我可能会因为这样说而感到愤怒:) 评论 2015年2月5日0:12
  • 86
    我强烈建议读这篇文章Bash常见问题关于主题。 评论 2016年1月30日2:22
  • @osirisgothra任何人如果认为解决文件名中的危险字符的方法不是编写健壮的脚本,而是阻止人们(可能在某些时候创建危险名称)访问系统,那将是很困难的。
    – 街童
    评论 5月3日4:15

77个答案77

重置为默认值
8118
#!/usr/bin/env bash(usr/bin/env bash)SCRIPT_DIR=$(cd--“$(目录名--”${BASH_SOURCE[0]}“)”&>/dev/null&&pwd)

是一个有用的单行程序,它将为您提供脚本的完整目录名,无论从何处调用脚本。

只要用于查找脚本的路径的最后一个组件不是符号链接(目录链接可以),它就会工作。如果还想解析到脚本本身的任何链接,则需要多行解决方案:

#!/usr/bin/env-bashSOURCE=${BASH_SOURCE[0]}而[-L“$SOURCE”];执行#resolve$SOURCE操作,直到文件不再是符号链接DIR=$(cd-P“$(目录名”$SOURCE“)”>/dev/null 2>&1&pwd)SOURCE=$(readlink“$SOURCE”)[[$SOURCE!=/*]]&&SOURCE=$DIR/$SOURCE#如果$SOUREC是相对符号链接,则需要相对于符号链接文件所在的路径进行解析完成DIR=$(cd-P“$(目录名”$SOURCE“)”>/dev/null 2>&1&pwd)

最后一个可以处理任何别名组合,来源,bash-c、符号链接等。

当心:如果你光盘在运行此代码段之前,返回到其他目录,结果可能不正确!

此外,请注意$CDPATH(美元)哥特查和stderr输出副作用,如果用户巧妙地重写了cd以将输出重定向到stderr(包括转义序列,例如调用时update_terminal_cwd>&2Mac上)。正在添加>/偏差/空值2>&1在你的结尾光盘命令将处理这两种可能性。

要了解其工作原理,请尝试运行以下更详细的表单:

#!/usr/bin/env-bashSOURCE=${BASH_SOURCE[0]}而[-L“$SOURCE”];执行#resolve$SOURCE操作,直到文件不再是符号链接目标=$(readlink“$SOURCE”)如果[[$TARGET==/*]];然后echo“SOURCE“$SOURCE”是到“$TARGET”的绝对符号链接”SOURCE=$目标其他的DIR=$(目录名“$SOURCE”)echo“SOURCE“$SOURCE”是到“$TARGET”的相对符号链接(相对于“$DIR”)”SOURCE=$DIR/$TARGET#如果$SOURCE是相对符号链接,我们需要相对于符号链接文件所在的路径来解析它fi(菲涅耳)完成echo“SOURCE是‘$SOURCE’”RDIR=$(目录名“$SOURCE”)DIR=$(cd-P“$(目录名“$SOURCE”)”>/dev/null 2>1&&pwd)如果[“$DIR”!=“$RDIR”];然后echo“DIR'$RDIR'解析为'$DIR'”fi(菲涅耳)echo“DIR是'$DIR'”

它将打印如下内容:

来源”/“scriptdir.sh”是与“sym2/scriptdir.ch”相对的符号链接(相对于“.”)SOURCE为“/sym2/scriptdir.sh’方向/sym2'解析为“/home/ubuntu/dotfiles/fo/real/real1/real2”DIR是“/home/ubuntu/dotfiles/fo fo/real/real1/real2”
16
  • 39
    您可以将此方法与用户25866的答案结合起来,得出一个适用于的解决方案源<脚本>bash<脚本>:DIR=“$(cd-P”$(目录名“${BASH_SOURCE[0]}”)“&&pwd)”. 评论 2011年10月19日15:54
  • 25
    有时光盘把东西打印到STDOUT!例如,如果您的$CDPATH(美元).。要覆盖此情况,请使用DIR=“$(cd”$(目录名“${BASH_SOURCE[0]}”)“>/dev/null&&pwd)” 评论 2013年2月3日2:33
  • 255
    这个公认的答案是不好的,它不适用于符号链接,并且过于复杂。目录名$(readlink-f$0)是正确的命令。请参见gist.github.com/tvlooy/cbfbdb111a4ebad8b93e用于测试用例 评论 2015年6月9日19:32
  • 214
    @tvlooy IMO你的答案也不完全正确,因为当路径中有空格时,它会失败。与换行符相比,这种情况并不罕见。目录名“$(readlink-f”$0“)”不会增加复杂性,而且公平地说,对于最少的麻烦来说,它更健壮。 评论 2015年10月28日23:38
  • 20
    @电视台目录名“$(readlink-f”$0“)”尽管在脚本来源的情况下失败,例如:/bin/bash-c。脚本.sh…使用$(目录名“$(readlink-f”${BASH_SOURCE[0]}“)”)相反 评论 2022年4月18日4:12
1257

使用目录名“$0”:

测试.sh:

#!/usr/bin/env-bashecho“您正在运行的脚本有:”echo“basename:[$(basename“$0”)]”echo“目录名:[$(目录名“$0”)]”echo“密码:[$(密码)]”

使用密码如果您没有从包含脚本的目录中运行脚本,那么单独运行脚本将不起作用。

[~]美元普华永道/主页/哑光[~]$ ./试验.sh您正在运行的脚本具有:基本名称:[test.sh]目录名:[/home/matt]密码:[/home/matt][~]美元cd/tmp[~/tmp]$~/test.sh您正在运行的脚本具有:基本名称:[test.sh]目录名:[/home/matt]密码:[/tmp]
10
  • 35
    对于bash以外的可移植性,0美元可能并不总是足够的。如果在路径上找到命令,您可能需要替换为“type-p$0”以使其生效。
    – 达伦
    评论 2008年10月23日20:15
  • 14
    @达伦:你只能用类型-p如果脚本是可执行的。如果使用bash测试2.sh还有另一个同名的脚本可在其他地方执行。
    – D.肖利
    评论 2010年2月5日12:18
  • 126
    @达伦:但既然问题被标记了猛击hash-bang行明确提到/垃圾桶/垃圾桶我想说,依靠羞辱是相当安全的。 评论 2010年6月11日12:56
  • 51
    +1,但使用时的问题目录名$0如果目录是当前目录,您将得到.。这很好,除非您要更改脚本中的目录并期望使用从中获得的路径目录名$0好像这是绝对的。要获得绝对路径:pushd `dirname$0`>/dev/null,SCRIPTPATH=`pwd`,popd>/dev/null:馅饼.org/1489386(但是当然有更好的方法来扩展这条路径吗?) 评论 2011年1月23日10:30
  • 13
    @T。J.克劳德我不确定目录名$0如果您将其分配给一个变量,然后使用它启动一个类似于$目录/脚本.sh; 我可以想象,90%的时间里,这就是这种类型事情的用例。./script.sh会很好的。
    – 亚光b
    评论 2011年1月24日12:55
683

这个目录名命令是最基本的,只需解析$0(脚本名称)变量:

目录名--“$0”;

但是,作为亚光b指出,根据脚本的调用方式,返回的路径是不同的。密码不会执行此操作,因为这只会告诉您当前目录是什么,而不是脚本所在的目录。此外,如果执行指向脚本的符号链接,您将获得链接所在位置的(可能是相对的)路径,而不是实际的脚本。

其他一些人提到了读取链接命令,但最简单的是,您可以使用:

目录名--“$(readlink-f--”$0“;)”;

readlink(读链接)将脚本路径解析为文件系统根的绝对路径。因此,任何包含单点或双点、波浪线和/或符号链接的路径都将解析为完整路径。

下面的脚本演示了其中的每一个,什么事:

#!/usr/bin/env-bashecho“密码:`pwd`”echo“\$0:$0”echo“basename:`basename--”$0“`”echo“目录名:`dirname--”$0“`”echo“目录名/readlink:$(目录名--“$(readlink-f--”$0“;)”;)”

在我的home目录中运行此脚本,使用相对路径:

>>>$ ./什么事密码:/Users/phatblat$0: ./什么事基本名称:whatdir.sh目录名:。目录名/readlink:/Users/phatblat

同样,但使用脚本的完整路径:

>>>$/Users/phatblat/whatdir.sh密码:/Users/phatblat$0:/Users/phatblat/whatdir.sh基本名称:whatdir.sh目录名:/Users/phatblat目录名/readlink:/Users/phatblat

正在更改目录:

>>>美元cd/tmp>>>$~/whatdir.sh美元密码:/tmp$0:/Users/phatblat/whatdir.sh基本名称:whatdir.sh目录名:/Users/phatblat目录名/readlink:/Users/phatblat

最后使用符号链接来执行脚本:

>>>$ln-s~/whatdir.sh whatdirlink.sh>>>$ ./什么dirlink.sh密码:/tmp$0: ./什么dirlink.sh基本名称:whatdirlink.sh目录名:。目录名/readlink:/Users/phatblat

然而,有一种情况下,当脚本是在bash中源代码(而不是执行)时,这种方法不起作用:

>>>美元cd/tmp>>>$ . ~/什么事密码:/tmp0美元:猛击基本名称:bash目录名:。目录名/readlink:/tmp
14
  • 20
    readlink(读链接)在默认安装中,在某些平台上不可用。如果可以,尽量避免使用
    – 总长度
    评论 2012年1月11日9:14
  • 55
    小心引用所有内容以避免出现空白问题:export SCRIPT_DIR=“$(目录名”$(readlink-f“$0”)”
    – 猫鼬
    评论 2013年9月17日19:40
  • 16
    在OSX Yosemite 10.10.1中-(f)不被视为readlink(读链接).使用stat-f相反,它会完成任务。谢谢 评论 2014年11月26日9:29
  • 12
    在OSX中,有格雷德林克,基本上是readlink(读链接)我们都很熟悉。以下是一个独立于平台的版本:dir=`greadlink-f${BASH_SOURCE[0]}||readlink-f${BAS_SOURCE[0]}`
    – 罗伯特
    评论 2016年1月14日20:16
  • 9
    请注意$0如果文件是源文件,则不起作用。你会得到-猛击而不是脚本名称。 评论 2016年6月30日16:52
205
pushd.>”(推送>)/dev/null';SCRIPT_PATH=“${BASH_SOURCE[0]:-$0}”;而[-h“$SCRIPT_PATH”];cd“$(目录名--”$SCRIPT_PATH“;)”;SCRIPT_PATH=“$(readlink-f--”$SCRIPT-PATH“;)”;完成cd“$(目录名--”$SCRIPT_PATH“;)”>'/dev/null';SCRIPT_PATH=“$(pwd;)”;popd>“/dev/null”;

它适用于所有版本,包括

  • 当通过多深度软链接调用时,
  • 当归档时
  • 当命令调用脚本时“来源“又名.(点)运算符。
  • 当arg$0从调用方修改。
  • “./script”
  • “/full/path/to/script”
  • “/some/path/../../ather/path/script”
  • “./some/folder/script”

或者,如果Bash脚本本身是相对符号链接希望要跟随它并返回链接到脚本的完整路径,请执行以下操作:

pushd.>”(推送>)/dev/null';SCRIPT_PATH=“${BASH_SOURCE[0]:-$0}”;而[-h“$SCRIPT_PATH”];cd“$(目录名--”$SCRIPT_PATH“;)”;SCRIPT_PATH=“$(readlink-f--”$SCRIPT-PATH“;)”;完成cd“$(目录名--”$SCRIPT_PATH“;)”>'/dev/null';SCRIPT_PATH=“$(pwd;)”;popd>“/dev/null”;

脚本_路径以完整路径给出,无论如何调用。

只需确保在脚本的开头找到它。

7
  • 4
    很好!可以将“pushd[…]popd/dev/null”替换为SCRIPT_PATH=readlink-f$(目录名“${VIRTUAL_ENV}”);
    – 位代码
    评论 2009年11月29日11:34
  • 1
    这是迄今为止我见过的最“稳定”的版本。谢谢您! 评论 2010年1月26日8:19
  • 5
    而不是使用pushd。。。;使用$(cd不是更好吗目录名“${SCRIPT_PATH}”&&密码)?但无论如何,剧本很棒!
    – 椭圆形
    评论 2010年8月18日10:16
  • 7
    脚本执行以下操作是危险的光盘走出当前目录,希望光盘稍后再次返回:脚本可能没有权限将目录更改回调用时的当前目录。(pushd/popd也是如此) 评论 2012年11月6日1:15
  • 8
    readlink-f特定于GNU。BSD公司readlink(读链接)没有该选项。 评论 2014年6月3日16:48
149

下面是一个易于记忆的脚本:

DIR=“$(目录名--”${BASH_SOURCE[0]}“;)”;#获取目录名DIR=“$(realpath-e--”$DIR“;)”;#如果需要,解析其完整路径
9
  • 14
    或者更隐晦地说,在一行:DIR=$(realpath“$(目录名”${BASH_SOURCE[0]}“)”) 评论 2019年4月4日11:55
  • 11
    为什么这不是公认的答案?使用有什么不同吗真实路径通过循环“手动”解决readlink(读链接)? 即使是readlink(读链接)手册页上说注意,realpath(1)是用于规范化功能的首选命令。 评论 2020年3月25日1:14
  • 6
    顺便说一下,我们不应该申请真实路径之前目录名,而不是之后?如果脚本文件本身是符号链接。。。它会给出如下内容DIR=“$(目录名”$(realpath“${BASH_SOURCE[0]}”)”。实际上非常接近西蒙提出的答案。 评论 2020年3月25日1:32
  • 2
    @我认为接受的用户9123是尝试与所有流行的shell/distro兼容。此外,根据您尝试执行的操作,在大多数情况下,人们希望获得符号链接所在的目录,而不是实际源的目录。
    – 
    评论 2020年5月24日12:23
  • 1
    我的真实路径(和man7.org/linux/man-pages/man1/realpath.1.html)没有-(f)期权;那应该是吗-e(电子)@维维蒂斯?(该用户添加了它。) 评论 2022年10月20日16:23
145

你可以使用$BASH_SOURCE(美元_来源):

#!/usr/bin/env-bashscriptdir=“$(目录名--”$BASH_SOURCE“;)”;

请注意,您需要使用#!/垃圾桶/垃圾桶而不是#!/垃圾桶/桶因为它是Bash扩展。

  • 19
    当我这样做的时候./foo/script,然后$(目录名$BASH_SOURCE)./foo文件.
    – 直到
    评论 2010年10月25日17:06
  • 5
    @直到,在这种情况下,我们可以使用真实路径命令获取的完整路径/foo/script。所以目录名$(realpath./foo/script)将给出脚本的路径。 评论 2019年12月17日5:17
  • BASH_源是一个数组,所以我不确定这是否正确。所有其他答案都使用BASH_SOURCE[0](BASH_SOURCE[0])这似乎更有道理。 评论 昨天
126

简短回答:

“`目录名--”$0“;`”

或(更可取地):

“$(目录名--”$0“;)”
7
  • 24
    如果您源代码为脚本,它将无法工作。“源my/script.sh” 评论 2014年2月5日7:34
  • 我一直在我的bash脚本中使用这一点,这些脚本自动执行任务,并经常调用同一目录中的其他脚本来源关于这些和cd$(目录名$0)很容易记住。
    – 这个
    评论 2017年1月3日16:28
  • 21
    @视频:${BASH_SOURCE[0]}而不是$0将与一起工作源my/script.sh 评论 2017年9月27日6:48
  • 2
    @如果来源于除bash之外的任何其他shell,TimothyJones将100%失败。${BASH_SOURCE[0]}一点也不令人满意。${BASH_SOURCE:-0}好多了。 评论 2018年11月13日18:43
  • 1
    @NamGVU是的,没错。不过,这个问题是关于Bash的 评论 2023年1月10日8:25
122

应该这样做:

DIR=“$(目录名”$(realpath“$0”)”)“

这适用于路径中的符号链接和空格。

请参阅的手册页目录名真实路径.

请添加关于如何支持MacOS的评论。对不起,我可以核实。

8
  • 10
    使用您的解决方案,调用如下脚本./script.sh显示.而不是完整的目录路径 评论 2016年6月14日18:27
  • 6
    MacOS上的readlink没有-f选项。使用斯达而不是。但它仍然表明.如果你在这个方向。 评论 2016年11月21日9:55
  • 您需要安装coreutils公司来自自制和使用格雷德林克以获得-(f)选项,因为它是*BSD,而不是Linux。
    – 龙788
    评论 2019年4月22日15:59
  • 1
    @MarkGates错了realpath:找不到命令在vanilla macOS上,我想你已经安装了一些自制软件包,让它在你的mac上运行
    – 纽克特
    评论 2021年11月4日20:08
  • 2
    $BASH_SOURCE(美元_来源)在我的.bashrc虽然$0不会 评论 2022年1月5日13:05
76

密码可用于查找当前工作目录,以及目录名查找特定文件的目录(运行的命令是$0,所以目录名$0应提供当前脚本的目录)。

然而,目录名精确地给出了文件名的目录部分,这很可能是相对于当前工作目录的。如果脚本由于某种原因需要更改目录,那么目录名变得毫无意义。

我建议如下:

#!/usr/bin/env-bashreldir=“$(目录名--”$0“;)”;cd“$reldir”;目录=“$(pwd;)”;echo“目录是${Directory}”;

这样,您将得到一个绝对目录,而不是相对目录。

由于脚本将在单独的Bash实例中运行,因此以后不需要恢复工作目录,但如果出于某种原因确实想在脚本中进行更改,可以很容易地将密码在更改目录之前将其转换为变量,以供将来使用。

虽然只是

cd“$(目录名--“$0”;)”;

解决了问题中的特定场景,我发现拥有绝对路径通常更有用。

2
  • 12
    您可以在一行中完成这一切:DIRECTORY=$(cd目录名$0&&压水)
    – 山茱萸
    评论 2008年10月29日8:38
  • 1
    如果脚本源于另一个脚本,并且您想知道后者的名称,则此操作不起作用。 评论 2014年3月28日13:10
43

这将获取上的当前工作目录Mac OS X v10.6.6(雪豹):

DIR=$(cd“$(目录名”$0“)”;密码)
1
  • 所以这在Linux上不起作用?
    – 纽克特
    评论 2021年11月4日20:11
42
SCRIPT_DIR=$(cd${0%/*}&&pwd-P)
2
  • 7
    正如前面的许多答案所详细解释的那样$0也不是密码保证具有正确的信息,这取决于脚本的调用方式。 评论 2013年9月23日16:51
  • @IMSoP,错了。“前面的许多答案”解释说,pwd返回当前目录,在本例中,目录在调用之前发生了更改。对于$0,在大多数情况下,都希望使用$0而不是$BASH_SOURCE,例如,如果需要的脚本源库确实确定了脚本的完整路径(而不是库)。
    – 亚历克
    评论 2023年1月19日3:00
41

我认为这并不像其他人想象的那么容易。密码不起作用,因为当前目录不一定是包含脚本的目录。$0也不总是有信息。考虑以下三种调用脚本的方法:

./script/usr/bin/script脚本

第一种和第三种方式$0没有完整的路径信息。在第二和第三阶段,密码不起作用。用第三种方法获取目录的唯一方法是遍历路径并找到正确匹配的文件。基本上,代码必须重做操作系统的功能。

实现您所要求的一种方法是将数据硬编码到/usr/共享目录,并通过其完整路径引用它。数据不应该在/usr/bin(用户/二进制)目录,所以这可能是要做的事情。

  • 9
    如果你打算反驳他的评论,证明脚本可以访问它与代码示例一起存储的地方。 评论 2015年11月18日18:54
  • 你试过了吗$0确实具有第三种情况的完整路径信息。
    – 亚历克
    评论 2023年1月19日3:03
  • 要获取包含它的目录的Bash脚本中的完整路径信息,请使用以下命令mydir=$(实际路径$0)。我已经测试过它在你描述的所有情况下都有效。 评论 4月26日10:01
38
$(目录名“$(readlink-f”$BASH_SOURCE“)”)
2
  • 我更喜欢,$BASH_SOURCE(美元_来源)结束$0,因为即使对于不熟悉bash的读者来说,它也是显式的。$(目录名--“$(readlink-f--”$BASH_SOURCE“)”) 评论 2020年1月8日13:04
  • 也,$BASH_SOURCE(美元_来源)在以下时间工作$0在我的情况下不会.bashrc(同时使用符号链接和来源) 评论 2022年1月5日13:03
30

这是特定于Linux的,但您可以使用:

SELF=$(readlink/proc/$$/fd/255)
2
  • 1
    它也是特定于bash的,但bash的行为可能已经改变了?/程序/fd/$$/255似乎指向tty,而不是目录。例如,在我当前的登录shell中,文件描述符0、1、2和255都引用/开发/pts/4无论如何,bash手册没有提到fd 255,因此依赖于此行为可能是不明智的\ 评论 2015年3月29日0:17
  • 交互式外壳!=脚本。无论如何实路径${BASH_SOURCE[0]};这似乎是最好的方式。 评论 2015年4月6日12:52
27

下面是一个符合POSIX的单行程序:

SCRIPT_PATH=`dirname“$0”`;SCRIPT_PATH=`eval“cd\”$SCRIPT-PATH\“&&pwd”`#测试echo$SCRIPT_PATH
2
  • 5
    在单独运行脚本或使用sudo运行脚本时,我成功地做到了这一点,但在调用源代码时却没有做到/脚本.sh 评论 2013年4月17日21:57
  • 光盘配置为打印新路径名。 评论 2013年11月4日13:45
27

最简单、最优雅的方法是:

#!/垃圾桶/垃圾桶目录=$(cd`dirname$0`&&pwd)echo$目录

这可以在所有平台上工作,而且非常干净。

有关更多详细信息,请参阅“bash脚本在哪个目录中?".

1
  • 1
    很好的干净解决方案,但如果文件是符号链接的,这将不起作用。
    – 鲁特
    评论 2019年9月26日8:43
26

对于Python,请参见我的另一个答案.

对于Bash,请参见以下内容:

总结:

FULL_PATH_TO_SCRIPT=“$(realpath”${BASH_SOURCE[-1]}“)”#或者,如果您不需要它也适用于**源**脚本:#FULL_PATH_TO_SCRIPT=“$(realpath“$0”)”#或者,如果是嵌套的“源”调用,则取决于所需的路径#FULL_PATH_TO_SCRIPT=“$(实路径“${BASH_SOURCE[0]}”)”#或者,添加“-s”以不扩展路径中的符号链接:#FULL_PATH_TO_SCRIPT=“$(realpath-s”${BASH_SOURCE[-1]}“)”SCRIPT_DIRECTORY=“$(目录名“$FULL_PATH_TO_SCRIPT”)”SCRIPT_FILENAME=“$(基本名称“$FULL_PATH_TO_SCRIPT”)”

细节:

如何获得完整文件路径,完整目录,以及基本文件名任何脚本运行来源...

……即使从另一个bash函数或脚本中调用调用的脚本,或使用嵌套源时!

在许多情况下,您所需要获取的只是刚刚调用的脚本的完整路径。这可以很容易地使用真实路径。请注意真实路径是的一部分GNU coreutils公司。如果你还没有安装它(它在Ubuntu上是默认的),你可以用sudo-apt更新&&sudo-apt-install-coreutils.

获取脚本路径.sh(有关此脚本的最新版本,请参阅获取脚本路径.sh在我的eRCaGuy_hello_world公司回购):

#!/垃圾桶/垃圾桶#A.获取完整路径,并展开(向下走)符号链接#A.1、`“$0”仅在文件为**运行**时有效,但在文件为**source**时无效。#FULL_PATH_TO_SCRIPT=“$(realpath“$0”)”#A.2、`“${BASH_SOURCE[-1]}”`无论文件是源文件还是运行文件,甚至#如果脚本是从另一个bash函数中调用的!#注意:如果`“${BASH_SOURCE[-1]}”`不能满足您的要求,请使用#改为`“${BASH_SOURCE[0]}”`,以便从数组中获取第一个元素。FULL_PATH_TO_SCRIPT=“$(实路径“${BASH_SOURCE[-1]}”)”#B.1、`“$0”`仅在文件为**运行**时有效,但在文件为**来源**时无效。#FULL_PATH_TO_SCRIPT_KEEP_SYMLINKS=“$(realpath-s”$0“)”#B.2节`“${BASH_SOURCE[-1]}”`无论文件是源文件还是运行文件,甚至#如果脚本是从另一个bash函数中调用的!#注意:如果`“${BASH_SOURCE[-1]}”`不能满足您的要求,请使用#取而代之的是`“${BASH_SOURCE[0]}”`,以便从数组中获取第一个元素。FULL_PATH_TO_SCRIPT_KEEP_SYMLINKS=“$(realpath-s”${BASH_SOURCE[-1]}“)”#然后还可以获得目录的完整路径和基#文件名,如下所示:SCRIPT_DIRECTORY=“$(目录名“$FULL_PATH_TO_SCRIPT”)”SCRIPT_FILENAME=“$(基本名称“$FULL_PATH_TO_SCRIPT”)”#现在打印出来echo“FULL_PATH_TO_SCRIPT=\”$FULL_PATH_TO_SCRIPT\“”echo“SCRIPT_DIRECTORY=\”$SCRIPT_DIRECTORY\“”echo“SCRIPT_FILENAME=\”$SCRIPT-FILENAME\“”

关于的重要说明嵌套的来源电话:如果“${BASH_SOURCE[-1]}”上面并没有给你你想要的,试着用“${BASH_SOURCE[0]}”而不是。第一个(0)索引提供数组中的第一个条目,最后一个条目(-1)index提供数组中的最后一个条目。根据你想要什么,你可能真的想要第一个条目。我在采购时发现了这种情况~/.bashrc具有.~/.bashrc,其来源~/.bash_aliases具有.~/.bash_aliases,我想要真实路径(带有扩展的符号链接)到~/.bash_aliases文件,而不是~/.bashrc文件。因为这些是嵌套的 来源呼叫,使用“${BASH_SOURCE[0]}”给了我我想要的:通往~/.bash_aliases! 使用“${BASH_SOURCE[-1]}”然而,给了我我所做的want:扩展路径~/.bashrc.

命令和输出示例:

  1. 正在运行脚本:
    ~/GS/dev/eRCaGuy_hello_world/bash$/获取脚本路径.shFULL_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=“获取脚本路径.sh”
  2. 采购带有的脚本.获取脚本路径.sh源get_script_path.sh(结果与上面的完全相同,因为我使用了“${BASH_SOURCE[-1]}”在脚本中,而不是"$0"):
    ~/GS/dev/eRCaGuy_hello_world/bash$。获取脚本路径.shFULL_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=“获取脚本路径.sh”

如果您使用"$0"在脚本中,而不是“${BASH_SOURCE[-1]}”,当运行脚本,但这个不受欢迎的而在以下情况下输出采购脚本:

~/GS/dev/eRCaGuy_hello_world/bash$。获取脚本路径.shFULL_PATH_TO_SCRIPT=“/bin/bash”SCRIPT_DIRECTORY=“/bin”SCRIPT_FILENAME=“猛击”

而且,显然如果你使用“$BASH_SOURCE”而不是“${BASH_SOURCE[-1]}”,它会的如果脚本是从另一个bash函数中调用的,那么就可以工作。所以,使用“${BASH_SOURCE[-1]}”因此,这是最好的方法,因为它解决了这两个问题!请参阅以下参考资料。

之间的差异真实路径realpath-s:

请注意真实路径也可以成功地遍历符号链接以确定并指向其目标,而不是指向符号链接。如果你不想要这种行为(有时我不想要),那么添加-秒真实路径命令,使该行改为如下所示:

#获取完整路径,但不要展开(向下走)符号链接;在里面#换句话说:保留符号链接作为路径的一部分!FULL_PATH_TO_SCRIPT=“$(realpath-s”${BASH_SOURCE[-1]}“)”

这样,符号链接就不会展开。相反,它们在完整路径中作为符号链接保留为-is。

上面的代码现在是我的eRCaGuy_hello_world公司此处显示此文件中的回购:bash/获取脚本路径.sh。请参考并运行此文件,以获取路径中带有和不带OUT符号链接的完整示例。有关这两种情况下的输出示例,请参见文件底部。

参考文献:

  1. 如何检索给定相对路径的绝对路径
  2. 教会了我BASH_源变量:Unix和Linux:确定源shell脚本的路径
  3. 教我的BASH_源实际上是一个数组,我们希望它的最后一个元素能在函数中按预期工作(因此我使用“${BASH_SOURCE[-1]}”在我的代码中):Unix和Linux:确定源shell脚本的路径
  4. bash手册-->搜索BASH_源:

    BASH_源

    一个数组变量,其成员是源文件名,其中姓名数组变量已定义。shell函数${函数名[$i]}在文件中定义${BASH_SOURCE[$i]}并从呼叫${BASH_SOURCE[$i+1]}.

另请参见:

  1. 我对Python的回答是:如何获取当前正在执行的python文件的路径和名称?
  2. [我的回答]Unix和Linux:确定源shell脚本的路径
  • 2
    两者的区别是什么${BASH_SOURCE[-1]}${BASH_SOURCE[0]}? 我知道-1从数组中检索最后一个元素,并0检索第一个,但在哪种情况下,我要使用一个而不是另一个? 评论 2022年4月13日14:26
  • @ElieG。,查看我的关于嵌套源调用的重要说明答案中的部分。当一个脚本源于另一个脚本时,这与嵌套源有关。 评论 2022年4月13日15:20
  • @伊利·G。,我想是的。而且,我没有尝试,但就我而言,我认为索引1会给我同样的结果-1因为我认为数组中只有2个元素,所以这在两种情况下都是最后一个元素。 评论 2022年4月13日15:56
22
#!/垃圾桶/桶PRG=“0美元”#相对符号链接需要这个而[-h“$PRG”];PRG=`readlink“$PRG”`完成scriptdir=`dirname“$PRG”`
1
  • 1
    我还没有跨不同的系统进行测试。但对我来说,这个解决方案是一个至少在Ubuntu上立即有效的解决方案! 评论 2020年5月26日0:30
20

尝试使用:

real=$(realpath“$(目录名“$0”)”)
7
  • 1
    我想知道的是,为什么这种方式不好?这对我来说似乎不错,也很正确。有人能解释为什么它被否决了吗?
    – 寿亚
    评论 2012年8月28日15:16
  • 7
    realpath不是一个标准的实用程序。 评论 2013年5月13日12:06
  • 5
    在Linux上,realpath是一个标准实用程序(GNU coreutils包的一部分),但它不是bash内置的(即bash本身提供的函数)。如果您正在运行Linux,此方法可能会起作用,尽管我会将$0对于${BASH_SOURCE[0]}这样,该方法可以在任何地方工作,包括在函数中。 评论 2014年7月18日15:53
  • 5
    这个答案中的操作顺序是错误的。你需要第一解析符号链接,然后目录名因为的最后一部分$0可能是指向与符号链接本身不在同一目录中的文件的符号链接。这个答案中描述的解决方案只获取它存储符号链接的目录的路径,而不是目标的目录。此外,此解决方案缺少引用。如果路径包含特殊字符,它将不起作用。
    – 哈杰罗
    评论 2015年4月13日21:43
  • 4
    dir=“$(realpath”$(目录名“${BASH_SOURCE[0]}”)” 评论 2019年3月11日12:52
19

以下是简单、正确的方法:

actual_path=$(readlink-f“${BASH_SOURCE[0]}”)script_dir=$(目录名“$actual_path”)

说明:

  • ${BASH_SOURCE[0]}-脚本的完整路径。即使脚本是源代码,此值也将是正确的,例如。源<(echo“echo$0”)打印猛击,同时将其替换为${BASH_SOURCE[0]}将打印脚本的完整路径。(当然,这假设您可以依赖Bash。)

  • readlink-f-递归解析指定路径中的任何符号链接。这是一个GNU扩展,在(例如)BSD系统上不可用。如果你正在运行Mac,你可以使用Homebrew安装GNUcoreutils公司并用greadlink-f.

  • 当然了目录名获取路径的父目录。

1
  • 2
    greadlink-f不幸的是,当来源在Mac上编写脚本:( 评论 2019年4月30日23:50
19

我尝试了所有这些,但都没有奏效。一只离得很近,但它有一个小虫子,严重损坏了它;他们忘了用引号把路径括起来。

还有很多人认为你是从shell运行脚本,所以他们忘记了当你打开一个新脚本时,它默认是在你家里。

在上尝试此目录以获取大小:

/var/没人/思考/关于空间存在/在目录中/名称/这是你的文件。text

无论您在何处或以何种方式运行它,都可以实现:

#!/垃圾桶/垃圾桶echo“密码:`pwd`”echo“\$0:$0”echo“basename:`basename”$0“`”echo“目录名:`dirname”$0“`”

为了使其真正有用,下面介绍了如何更改到运行脚本的目录:

cd“`dirname”$0“`”
2
  • 4
    如果脚本来自另一个脚本,则不起作用。 评论 2014年3月28日13:12
  • 如果$0的最后一部分是指向另一个目录项的符号链接,则此操作无效(ln-s/bin64/foo/usr/bin/foo).
    – 哈杰罗
    评论 2020年3月17日11:20
18

这是对e-satis和3bcdnlklvc04a中指出的解决方案的轻微修改他们的答案:

SCRIPT_DIR=“”pushd“$(目录名”$(readlink-f“$BASH_SOURCE”)”)“>/dev/null&&{SCRIPT_DIR=“$PWD”popd>/dev/null}

这在他们列出的所有案例中都应该有效。

这将防止邻苯二胺失败后推送的感谢konsolebox。

5
  • 这非常适合获得“真实”目录名,而不仅仅是符号链接的名称。谢谢您!
    – 陶涵
    评论 2010年6月23日20:32
  • 1
    更好SCRIPT_DIR=“”;pushd“$(目录名”$(readlink-f“$BASH_SOURCE”)”)“>/dev/null&&{SCRIPT_DIR=$PWD;popd>/devnull;} 评论 2014年7月3日4:15
  • @konsolebox,你想防御什么?我通常喜欢内联逻辑条件,但您在pushd中看到的具体错误是什么?我宁愿找到一种直接处理它的方法,而不是返回空的SCRIPT_DIR。 评论 2015年1月19日20:03
  • @Fuwjax避免这样做的自然实践邻苯二胺在以下情况下(即使很少)推送的失败。万一推送的失败了,你认为它的价值是什么脚本_DIR? 操作可能会有所不同,这取决于看起来合乎逻辑的内容或用户可能喜欢的内容,但肯定是这样做的邻苯二胺是错误的。 评论 2015年1月20日19:21
  • 所有这些推送的 邻苯二胺只需将其放下并使用光盘+密码而是包含在命令替换中。SCRIPT_DIR=$(…) 评论 2020年5月27日3:16
17

我会使用这样的东西:

#检索被调用脚本的完整路径名scriptPath=$(其中$0)#检查路径是否为链接如果[-L$scriptPath];然后#它是一个链接,然后检索目标路径并获取目录名sourceDir=$(目录名$(readlink-f$scriptPath))其他的#否则,只需获取脚本路径的目录名sourceDir=$(目录名$scriptPath)fi(菲涅耳)
2
  • 这是真的!使用简单第页也是!简单的问题目录名“$0”基于解决方案:如果脚本位于$路径如果调用时没有路径,则会给出错误的结果。 评论 2014年11月18日10:25
  • @Notinlist不是这样。如果脚本是通过路径,$0将包含绝对文件名。如果使用包含/,$0将包含该内容。 评论 2016年2月3日22:08
17

对于具有GNU coreutils的系统readlink(读链接)(例如,Linux):

$(readlink-f“$(目录名“$0”)”)

没有必要使用BASH_源什么时候$0包含脚本文件名。

1
  • 4
    除非脚本源于。或“source”,在这种情况下,它仍然是来源于它的任何脚本,或者,如果是从命令行,则是“-bash”(tty登录)或“bash”(通过“bash-l”调用)或“/bin/bash”(作为交互式非登录shell调用) 评论 2015年2月5日0:07
14

$_值得一提的是$0。如果您正在从Bash运行脚本,可以将接受的答案缩短为:

DIR=“$(目录名”$_“)”

请注意,这必须是脚本中的第一条语句。

2
  • 4
    如果你来源.脚本。在这些情况下,$_将包含在..$BASH_SOURCE(美元_来源)每次都有效。
    – 咔哒声
    评论 2014年1月31日14:55
  • 这是波尔-就像!巧合? 评论 2021年1月20日3:44
14

难以置信的简单,无论你如何调用脚本:

#!/垃圾桶/垃圾桶#the_source=$(readlink-f${BASH_source[0]})dirname=$(目录名${thesource})echo“the_source:${the_source}”echo“用户名:${用户名}”

随时随地跑步:

用户@计算机:~/下载/temp$/试验.sh

输出:

the_source:/home/user/Downloads/temp/test.sh目录名:/home/user/Downloads/temp
1
  • 赞成这个答案,因为它告诉我们脚本到底在哪个目录中,无论是从符号链接还是原始脚本调用它。当您的脚本只是中的符号链接时,它尤其有用.箱子目录创建者npm/纱线您现在需要原始脚本位置来相对引用绑定在您的npm包中的静态资源。。。 评论 2023年7月19日15:41
13

以下是获取脚本信息的快捷方式:

文件夹和文件:

脚本:“/tmp/src-dir/test.sh”调用文件夹:“/tmp/src-dir/other”

使用以下命令:

echo Script-Dir:`dirname“$(realpath$0)”`echo脚本目录:$(cd${0%/*}&&pwd-P)echo脚本目录:$(目录名“$(readlink-f“$0”))回声echo脚本名称:`basename“$(realpath$0)”`echo脚本名称:`basename$0`回声echo Script-Dir-Relative:`目录名“$BASH_SOURCE”`echo Script-Dir-Relative:`dirname$0`回声echo Calling-Dir:`pwd`

我得到了这个输出:

脚本目录:/tmp/src-Dir脚本目录:/tmp/src-Dir脚本目录:/tmp/src-Dir脚本名称:test.sh脚本名称:test.sh脚本无关:。。脚本无关:。。调用目录:/tmp/src-Dir/other

另请参见:https://pastebin.com/J8KjxrPF网站

0
13

这在Bash 3.2中起作用:

路径=“$(目录名”$(其中“$0”)”)“

如果您有~/箱目录$路径,你有A类在此目录中。它来源于脚本~/bin/lib/B。您知道包含的脚本相对于原始脚本的位置,在图书馆子目录,但不是相对于用户当前目录的位置。

这可以通过以下方法解决(内部A类):

源“$(目录名”$(其中“$0”)”)/lib/B

用户在哪里或他/她如何调用脚本都无关紧要。这将永远有效。

2
  • 5
    上的点哪一个很有争议。类型,搞砸和其他内置程序在bash中做同样的事情更好。哪一个更便于携带,尽管它实际上并不一样哪一个在tcsh等其他shell中使用,它是一个内置程序。 评论 2014年1月13日22:30
  • 2
    “总是”?一点也不。哪一个作为一个外部工具,您没有理由相信它的行为与父shell相同。 评论 2014年6月9日3:42
10

我比较了许多给出的答案,并提出了一些更紧凑的解决方案。这些似乎可以处理您最喜欢的以下组合中出现的所有疯狂的边缘情况:

  • 绝对路径或相对路径
  • 文件和目录软链接
  • 调用为脚本,bash脚本,bash-c脚本,源脚本,或.script脚本
  • 目录和/或文件名中的空格、制表符、换行符、Unicode等
  • 以连字符开头的文件名

如果您是从Linux运行的,那么使用程序handle是定位当前运行的脚本的完全解析源的最佳解决方案(在交互式会话中,链接指向相应的/开发/pts/X):

解析=“$(readlink/proc/$$/fd/255&&echo X)”&&resolved=“${resolved%$'\nX'}”

这有点丑陋,但修复方法紧凑且易于理解。我们不只是使用bash原语,但我可以接受,因为readlink(读链接)大大简化了任务。这个回波X添加了X(X)到变量字符串的末尾,这样文件名中的任何尾随空格都不会被吃掉,并且参数替换${VAR%X}在这条线的末尾去掉X(X).因为readlink(读链接)添加了一个自己的换行符(如果不是因为我们以前的技巧,通常会在命令替换中使用),我们也必须去掉它。使用$''引用方案,它允许我们使用转义序列,例如\n个表示换行符(这也是您可以轻松创建命名不合理的目录和文件的方法)。

以上内容应该可以满足您在Linux上定位当前运行的脚本的需要,但如果您没有程序文件系统,或者如果您试图找到其他文件的完全解析路径,那么您可能会发现下面的代码很有用。这只是对上面的一句话的一点修改。如果您正在使用奇怪的目录/文件名,请同时使用这两个文件名检查输出最小二乘法readlink(读链接)信息丰富最小二乘法将输出“简化”路径,替换?对于换行之类的东西。

绝对路径=$(readlink-e--“${BASH_SOURCE[0]}”&&echo x)&&absolute_path=${绝对路径%?x}dir=$(目录名--“$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'“$absolte_path”
4
  • 我明白了/开发/pts/30在Ubuntu 14.10桌面上使用bash。 评论 2015年8月15日11:29
  • @DanDascalescu使用单衬里?还是底部的完整代码片段?你给它添加了什么复杂的路径名吗?
    – 比利jmc
    评论 2015年8月19日6:34
  • 一行加上另一行echo$已解决,我将其另存为d日,chmod+x d,./天. 评论 2015年8月20日5:55
  • @DanDascalescu脚本中的第一行需要#!/垃圾桶/垃圾桶
    – 比利jmc
    评论 2015年8月23日5:07
10

尝试以下交叉兼容解决方案:

CWD=“$(cd-P--”$(目录名--“${BASH_SOURCE[0]}”)“&&pwd-P)”

如以下命令真实路径readlink(读链接)可能不可用(取决于操作系统)。

注意:在Bash中,建议使用${BASH_SOURCE[0]}而不是$0,否则在源文件时路径可能会中断(来源/.).

或者,您可以在Bash中尝试以下功能:

realpath(){[[$1=/*]]&&回声“$1”||echo“$PWD/${1#./}”}

此函数采用一个参数。如果参数已经有绝对路径,则按原样打印,否则打印$PWD(美元)变量+文件名参数(不带./前缀)。

相关:

2
  • 2
    @克里斯真实路径函数接受1个参数。如果参数已经有绝对路径,请按原样打印,否则打印$PWD(美元)+文件名(不带./前缀)。 评论 2015年3月27日17:35
  • 2
    当脚本被符号链接时,交叉兼容解决方案不起作用。 评论 2015年9月8日20:59

不是你想要的答案吗?浏览标记的其他问题问你自己的问题.