我正在写一个bash脚本。我需要当前的工作目录始终是脚本所在的目录。
默认行为是,脚本中的当前工作目录是运行它的shell的目录,但我不希望出现这种行为。
- 您是否考虑过将包装脚本(如/usr/bin-to-cd)放入(硬编码)正确的目录,然后执行脚本?
- 为什么需要脚本的目录?也许有更好的方法来解决潜在的问题。
- 我想指出的是,您所称的"明显不受欢迎"的行为实际上是完全必要的——如果我运行myscript path/to/file,我希望脚本相对于当前目录评估路径/到/文件,而不是脚本所处的目录。另外,正如bash faq提到的那样,使用ssh remotehost bash < ./myscript运行的脚本会发生什么情况?
- bash脚本的可能副本可以告诉它存储在哪个目录中吗?
- cd"${BASH_SOURCE%/*}" || exit
1 2
| #!/bin/bash
cd"$(dirname"$0")" |
- 在Windows下使用bash时,dirname返回"."。所以,保罗的回答更好。
- 在Mac OSX中还返回"."
- 值得注意的是,如果一个符号链接构成$0的一部分,事情可能会破裂。在您的脚本中,您可能期望,例如,../../引用脚本位置上方两个级别的目录,但如果符号链接正在运行,则不一定如此。
- 在Ubuntu 14上返回"."。
- 如果您将脚本称为./script,那么.是正确的目录,而改为.,它也会出现在script所在的目录中,即当前工作目录中。
- 也许cd"$(dirname --"$0")"在处理$0以负号开头的情况时会更好一些。
- 作为一个说明,我会考虑使用pushd/popd,而不是cd。如果您的脚本像正常一样从终端执行,那么您是否更改PWD并不重要,因为它作为子进程运行,但是总是有可能有人正在使用该脚本,或者可能有其他工具将在同一进程中执行该脚本。
- 如果从当前目录运行脚本,如bash script.sh,那么$0的值为script.sh。cd命令对您"有效"的唯一方式是因为您不关心失败的命令。如果使用set -o errexit(aka:set -e)来确保脚本不会吹过失败的命令,那么这将不起作用,因为cd script.sh是一个错误。可靠的(bash特定的)方法是cd"$(dirname ${BASH_SOURCE[0]})"。
- @brunobronosky:dirname script.sh打印.,退出代码为0。因此执行cd .,同时退出退出代码为0。我不知道失败的命令会出现在哪里。
- 不知道前两条评论(Tvaroh和BenClayton)想说什么,但据我所见,Dirname返回''是很好的。在MacOSX中,这在所有情况下都可以正常工作,而Paul的答案(在工作时也返回".")对于当前目录中的"bash script.sh"不起作用。
- @dimpiax:对于名称中包含空格的目录,使用反勾号而不是正确的引用会失败。我恢复了你的编辑添加了一个基于倒勾的备选方案。
- 引号好像乱了。你不应该逃避内心的痛苦吗?cd"$(dirname "$0")"
- @柳伯斯拉夫,引号很好。它们不会作为字符串传递给某个子shell,由该子shell解析。外壳使用这些内部引号运行带有单个参数的dirname命令,而不以任何方式解释$0的值。然后,外部引号确保dirname的输出用作cd的单个参数,而不以任何方式解释字符串。
- 只是觉得可能值得一提的是,您可以使用"pushd"而不是cd,然后在脚本完成后允许您"popd"返回到它们启动脚本的位置。
- @气动:脚本作为一个单独的进程运行,一个进程的当前工作目录根本不影响任何其他进程的当前工作目录。
- @lyuboslavkanev,…也许你在考虑基于backtick的命令替换语法,在这里需要转义内部引号;对于$(...),情况并非如此。
以下内容也有效:
语法在这个stackoverflow答案中进行了详细描述。
- 在Windows上使用bash时,请使用$0%[/]*
- 听起来很有趣,但它不能在当前目录下工作(而是传递脚本的名称)。
- 说明其工作原理:stackoverflow.com/questions/6393551/&hellip;
- 如果路径包含空白,不要忘记用引号括起来。即cd"$0%/*"
- @Kenorb非常感谢你的链接
- 你如何处理一个有空间的目录??!如果目录是干净的,上面的代码就可以工作,但是如果目录有空间,它就会失败。
- @nus imho unreadable对于像这样的非常短的字符串来说是可以的,这些字符串总是被复制粘贴的,不需要真正理解。
- 如果使用bash script.sh调用脚本,则此操作失败--$0将只是文件名。
- 如果脚本在根目录中,则两者都不起作用,因为"$0%/*"将扩展为空字符串
- 在zsh中,这将启动一个新的shell。
- 我想说这个答案太简洁了。你几乎不懂语法。关于如何阅读这篇文章的一些描述会很有帮助。
- 这个把戏也不会产生一个子地狱,所以对速度怪胎来说是很好的。
尝试以下简单的一句话:
适用于所有UNIX/OSX/Linux
1
| dir=$(cd -P --"$(dirname --"$0")" && pwd -P) |
注意:命令中使用双破折号(--)表示命令选项的结束,因此包含破折号或其他特殊字符的文件不会破坏命令。
对于Linux、Mac和其他*BSD:
1
| cd"$(dirname"$(realpath"$0")")"; |
注意:默认情况下,realpath应该安装在最流行的Linux发行版中(如ubuntu),但在某些版本中,它可能会丢失,因此您必须安装它。
否则,您可以尝试类似的操作(它将使用第一个现有工具):
1
| cd"$(dirname"$(readlink -f"$0" || realpath"$0")")" |
对于特定于Linux的:
1
| cd"$(dirname"$(readlink -f"$0")")" |
在*bsd/mac上使用gnu readlink:
1
| cd"$(dirname"$(greadlink -f"$0")")" |
注意:您需要安装coreutils。(例如1)。安装自制,2.brew install coreutils。
在狂欢中
在bash中,可以使用参数扩展来实现这一点,比如:
但如果脚本是从同一个目录运行,它就不起作用。
或者,您可以在bash中定义以下函数:
1 2 3
| realpath () {
[[ $1 = /* ]] && echo"$1" || echo"$PWD/${1#./}"
} |
此函数接受1个参数。如果参数已经有绝对路径,则按原样打印,否则打印$PWD变量+文件名参数(不带./前缀)。
或者这里是从debian .bashrc文件中获取的版本:
1 2 3 4 5 6 7 8 9 10 11 12 13
| function realpath()
{
f=$@
if [ -d"$f" ]; then
base=""
dir="$f"
else
base="/$(basename"$f")"
dir=$(dirname"$f")
fi
dir=$(cd"$dir" && /bin/pwd)
echo"$dir$base"
} |
相关:
如何检测运行shell脚本的当前目录?
从脚本本身中获取bash脚本的源目录
带有OS X的bash脚本绝对路径
bash脚本获取完整路径的可靠方法
参见:
如何在Mac上获取GNU的readlink-f行为?
- 一个比流行的更好的答案,因为它解决了系统链接,并为不同的操作系统帐户。谢谢!
- 谢谢你的回答。最上面的那个在我的电脑上工作…但是--switch在cp命令中做了什么,@kenorb?
- 命令中使用双破折号(--表示命令选项的结束,因此包含破折号或其他特殊字符的文件不会破坏命令。例如,通过touch"-test"和touch -- -test创建文件,然后通过rm"-test"和rm -- -test删除文件,并查看不同之处。
- 如果可以的话,我会删除我的upvote:realpath不推荐使用unix.stackexchange.com/questions/136494/&hellip;
- @它说只有debian realpath包被否决,而不是gnu realpath包。如果您认为不清楚,可以建议进行编辑。
1
| cd"$(dirname"${BASH_SOURCE[0]}")" |
这很容易。它起作用了。
- 这应该是公认的答案。在OSX和LinuxUbuntu上工作。
- 当脚本B源于其他脚本A,并且您需要使用与脚本B相关的路径时,这非常有用。谢谢。
- 对于那些不幸不得不触摸窗户的人来说,这在Cygwin也很有效。
- 或cd"${BASH_SOURCE%/*}" || exit。
对于在其他地方没有符号链接的脚本(如$PATH中),接受的答案很好。
1 2
| #!/bin/bash
cd"$(dirname"$0")" |
但是,如果脚本是通过symlink运行的,
1 2
| ln -sv ~/project/script.sh ~/bin/;
~/bin/script.sh |
这将进入~/bin/目录,而不是~/project/目录,如果cd的目的是包括与~/project/相关的依赖项,则可能会破坏脚本。
symlink安全答案如下:
1 2
| #!/bin/bash
cd"$(dirname"$(readlink -f"${BASH_SOURCE[0]}")")" |
需要readlink -f来解析潜在符号链接文件的绝对路径。
需要引号来支持可能包含空白的文件路径(不好的做法,但假设不是这样做是不安全的)
这个脚本似乎对我有用:
1 2 3 4
| #!/bin/bash
mypath=`realpath $0`
cd `dirname $mypath`
pwd |
pwd命令行将脚本的位置作为当前工作目录进行回声,无论从何处运行它。
- realpath不太可能安装在任何地方。这可能无关紧要,取决于手术的情况。
- 我的系统没有realpath,但它有readlink,这看起来很相似。
- 在这种情况下,这些命令似乎很危险
- 默认情况下哪个dist没有安装realpath?拥有Ubuntu。
1
| cd"`dirname $(readlink -f ${0})`" |
- 你能解释一下你的答案吗?
- 尽管此代码可能有助于解决问题,但它不能解释为什么和/或如何回答问题。提供这种额外的背景将显著提高其长期教育价值。请编辑您的答案以添加解释,包括哪些限制和假设适用。
获取脚本的真正路径
1 2 3 4 5 6
| if [ -L $0 ] ; then
ME=$(readlink $0)
else
ME=$0
fi
DIR=$(dirname $ME) |
(这是我这里相同问题的答案:获取执行脚本的目录的名称)
pwd是一个环境变量。
如果您只需要打印当前的工作目录,那么您可以按此操作。
1 2 3 4 5
| $ vim test
#!/bin/bash
pwd
:wq to save the test file. |
授予执行权限:
然后通过./test执行脚本,就可以看到当前的工作目录。
- 问题是如何确保脚本在自己的目录中运行,包括该目录是否不是工作目录。所以这不是一个答案。不管怎样,为什么要这样做而不是…运行pwd?在我看来好像浪费了很多的按键。