bash 如何在 MacOS 上获取 shell 脚本的绝对路径名?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5756524/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to get absolute path name of shell script on MacOS?
提问by Ivan Balashov
readlink -f
does not exist on MacOS. The only working solution for Mac OS I managed to find on the net goes like this:
readlink -f
在 MacOS 上不存在。我在网上找到的唯一适用于 Mac OS 的解决方案是这样的:
if [[ $(echo ABSPATH=$(cd "$(dirname "-P Display the physical current working directory (all symbolic links resolved).
")"; pwd -P)
| awk '/^\//') == pushd . > /dev/null
SCRIPT_PATH="${BASH_SOURCE[0]}";
while([ -h "${SCRIPT_PATH}" ]) do
cd "`dirname "${SCRIPT_PATH}"`"
SCRIPT_PATH="$(readlink "`basename "${SCRIPT_PATH}"`")";
done
cd "`dirname "${SCRIPT_PATH}"`" > /dev/null
SCRIPT_PATH="`pwd`";
popd > /dev/null
echo "srcipt=[${SCRIPT_PATH}]"
echo "pwd =[`pwd`]"
]]; then
ABSPATH=$(dirname wget -o /dev/null -O - http://host.domain/dir/script.sh |bash
)
else
ABSPATH=$PWD/$(dirname #!/bin/bash
# setup test enviroment
mkdir -p dir1/dir2
mkdir -p dir3/dir4
ln -s ./dir1/dir2/foo bar
ln -s ./../../dir3/dir4/test.sh dir1/dir2/foo
ln -s ./dir1/dir2/foo2 bar2
ln -s ./../../dir3/dir4/doe dir1/dir2/foo2
cp test.sh ./dir1/dir2/
cp test.sh ./dir3/dir4/
cp test.sh ./dir3/dir4/doe
P="`pwd`"
echo "--- 01"
echo "base =[${P}]" && ./test.sh
echo "--- 02"
echo "base =[${P}]" && `pwd`/test.sh
echo "--- 03"
echo "base =[${P}]" && ./dir1/dir2/../../test.sh
echo "--- 04"
echo "base =[${P}/dir3/dir4]" && ./bar
echo "--- 05"
echo "base =[${P}/dir3/dir4]" && ./bar2
echo "--- 06"
echo "base =[${P}/dir3/dir4]" && `pwd`/bar
echo "--- 07"
echo "base =[${P}/dir3/dir4]" && `pwd`/bar2
echo "--- 08"
echo "base =[${P}/dir1/dir2]" && `pwd`/dir3/dir4/../../dir1/dir2/test.sh
echo "--- 09"
echo "base =[${P}/dir1/dir2]" && ./dir1/dir2/test.sh
echo "--- 10"
echo "base =[${P}/dir3/dir4]" && ./dir3/dir4/doe
echo "--- 11"
echo "base =[${P}/dir3/dir4]" && ./dir3/dir4/test.sh
echo "--- 12"
echo "base =[${P}/dir3/dir4]" && `pwd`/dir3/dir4/doe
echo "--- 13"
echo "base =[${P}/dir3/dir4]" && `pwd`/dir3/dir4/test.sh
echo "--- 14"
echo "base =[${P}/dir3/dir4]" && `pwd`/dir1/dir2/../../dir3/dir4/doe
echo "--- 15"
echo "base =[${P}/dir3/dir4]" && `pwd`/dir1/dir2/../../dir3/dir4/test.sh
echo "--- 16"
echo "base s=[${P}]" && source test.sh
echo "--- 17"
echo "base s=[${P}]" && source `pwd`/test.sh
echo "--- 18"
echo "base s=[${P}/dir1/dir2]" && source ./dir1/dir2/test.sh
echo "--- 19"
echo "base s=[${P}/dir3/dir4]" && source ./dir1/dir2/../../dir3/dir4/test.sh
echo "--- 20"
echo "base s=[${P}/dir3/dir4]" && source `pwd`/dir1/dir2/../../dir3/dir4/test.sh
echo "--- 21"
pushd . >/dev/null
cd ..
echo "base x=[${P}/dir3/dir4]"
./`basename "${P}"`/bar
popd >/dev/null
)
fi
Can anyone suggest anything more elegant to this seemingly trivial task?
任何人都可以对这个看似微不足道的任务提出更优雅的建议吗?
回答by Gordon Davisson
Another (also rather ugly) option:
另一个(也相当丑陋)选项:
cd "$(dirname -- "$ realpath bin
/Users/nhed/bin
")"
dir="$PWD"
echo "$dir"
cd - > /dev/null
From pwd
man page,
从pwd
手册页,
echo $(pwd)/"/home/barun/codes/ns2/link_down/./test.sh
"
回答by GreenFox
Get absolute path of shell script
获取shell脚本的绝对路径
Dug out some old scripts from my .bashrc, and updated the syntax a bit, added a test suite.
从我的 .bashrc 中挖出一些旧脚本,并稍微更新了语法,添加了一个测试套件。
Supports
支持
- source ./script (When called by the
.
dot operator) - Absolute path /path/to/script
- Relative path like ./script
- /path/dir1/../dir2/dir3/../script
- When called from symlink
- When symlink is nested eg)
foo->dir1/dir2/bar bar->./../doe doe->script
- When caller changes the scripts name
- source ./script(当由
.
点运算符调用时) - 绝对路径 /path/to/script
- /script 之类的相对路径
- /path/dir1/../dir2/dir3/../script
- 从符号链接调用时
- 当符号链接嵌套时,例如)
foo->dir1/dir2/bar bar->./../doe doe->script
- 当调用者更改脚本名称时
It has been tested and used in real projects with success, however there may be corner cases I am not aware of.
If you were able to find such a situation, please let me know.
(For one, I know that this does not run on the sh shell)
它已经在实际项目中进行了成功的测试和使用,但是可能存在我不知道的极端情况。
如果你能找到这样的情况,请告诉我。
(一方面,我知道这不会在 sh shell 上运行)
Code
代码
ABSPATH=$(perl -MCwd=realpath -e "print realpath ''")# detect if GNU readlink is available on OS X if [ "$(uname)" = "Darwin" ]; then which greadlink > /dev/null || { printf 'GNU readlink not found\n' exit 1 } alias readlink="greadlink" fi # create a $dirname variable that contains the file dir dirname=$(dirname "$(readlink -f "
")") # use $dirname to find a relative file cat "$dirname"/foo/bar.txt#!/bin/bash # This was re-worked on 2018-10-26 after der@build correctly # observed that the previous version did not work. # Works on both linux and Mac OS X. # The "pwd -P" re-interprets all symlinks. function read-link() { local path= if [ -d $path ] ; then local abspath=$(cd $path; pwd -P) else local prefix=$(cd $(dirname -- $path) ; pwd -P) local suffix=$(basename $path) local abspath="$prefix/$suffix" fi if [ -e $abspath ] ; then echo $abspath else echo 'error: does not exist' fi } # Example usage. while (( $# )) ; do printf '%-24s - ' "" read-link shift done
Known issuse
已知问题
Script must be on disk somewhere, let it be over a network. If you try to run this script from a PIPE it will not work
脚本必须在某个地方的磁盘上,让它通过网络。 如果您尝试从 PIPE 运行此脚本,它将不起作用
$ ./example.sh /usr/bin/which /bin/which /etc/racoon ~/Downloads
/usr/bin/which - /usr/bin/which
/bin/which - error: does not exist
/etc/racoon - /private/etc/racoon
/Users/jlinoff/Downloads - /Users/jlinoff/Downloads
Technically speaking, it is undefined.
Practically speaking, there is no sane way to detect this.
从技术上讲,它是未定义的。
实际上,没有理智的方法可以检测到这一点。
Test case used
使用的测试用例
And the current test case that check that it works.
以及检查它是否有效的当前测试用例。
$ ./example.sh /usr/bin/which /bin/whichx /etc/init.d ~/Downloads
/usr/bin/which - /usr/bin/which
/bin/whichx - error: does not exist
/etc/init.d - /etc/init.d
/home/jlinoff/Downloads - /home/jlinoff/Downloads
PurpleFox aka GreenFox
PurpleFox 又名 GreenFox
回答by Lynch
Using bash I suggest this approach. You first cd to the directory, then you take the current directory using pwd. After that you must return to the old directory to ensure your script does not create side effects to an other script calling it.
使用 bash 我建议这种方法。您首先 cd 到目录,然后使用 pwd 获取当前目录。之后,您必须返回到旧目录以确保您的脚本不会对调用它的其他脚本产生副作用。
abspath ()
{
case "" in
[./]*)
local ABSPATH="$(cd ${1%/*}; pwd)/${1##*/}"
echo "${ABSPATH/\/\///}"
;;
*)
echo "${PWD}/"
;;
esac
}
This solution is safe with complex path. You will never have troubles with spaces or special charaters if you put the quotes.
该解决方案对于复杂路径是安全的。如果你加上引号,你永远不会遇到空格或特殊字符的问题。
Note: the /dev/null is require or "cd -" print the path its return to.
注意: /dev/null 是 require 或“cd -”打印其返回的路径。
回答by nhed
Also note that homebrew
's (http://brew.sh) coreutils
package includes realpath
(link created in/opt/local/bin
).
还要注意homebrew
's ( http://brew.sh)coreutils
包包括realpath
(在 中创建的链接/opt/local/bin
)。
回答by Barun
Can you try something like this inside your script?
你能在你的脚本中尝试这样的事情吗?
##代码##In my machine it shows:
在我的机器上显示:
##代码##which is the absolute path name of the shell script.
这是shell脚本的绝对路径名。
回答by William Pursell
If you don't mind using perl:
如果您不介意使用 perl:
##代码##回答by Yoshua Wuyts
I've found this to be useful for symlinks / dynamic links - works with GNU readlink only though (because of the -f flag):
我发现这对符号链接/动态链接很有用 - 尽管只适用于 GNU readlink(因为 -f 标志):
##代码##回答by Nathan Weeks
If you're using ksh, the ${.sh.file}
parameter is set to the absolute pathname of the script. To get the parent directory of the script: ${.sh.file%/*}
如果您使用 ksh,则该${.sh.file}
参数设置为脚本的绝对路径名。要获取脚本的父目录:${.sh.file%/*}
回答by Joe Linoff
I use the function below to emulate "readlink -f" for scripts that have to run on both linux and Mac OS X.
我使用下面的函数为必须在 linux 和 Mac OS X 上运行的脚本模拟“readlink -f”。
##代码##This is the output for some common Mac OS X targets:
这是一些常见 Mac OS X 目标的输出:
##代码##The is the output for some linux targets.
这是一些 linux 目标的输出。
##代码##回答by nhed
this is what I use, may need a tweak here or there
这是我使用的,可能需要在这里或那里进行调整
##代码##This is for any file - and of curse you can just invoke it as abspath ${0}
这适用于任何文件 - 诅咒你可以调用它 abspath ${0}
The first case deals with relative paths by cd-ing to the path and letting pwd figure it out
第一种情况通过 cd-ing 到路径并让 pwd 找出来处理相对路径
The second case is for dealing with a local file (where the ${1##/} would not have worked)
第二种情况是处理本地文件( ${1##/} 不起作用)
This does NOT attempt to undo symlinks!
这不会尝试撤消符号链接!