Bash脚本绝对path与OSX

我正在尝试获取OS X上正在运行的脚本的绝对path。

我看到许多回复为readlink -f $0 。 但是,由于OS X的readlink与BSD相同,它只是不起作用(它与GNU的版本一起工作)。

任何build议,为此开箱即用的解决scheme?

有一个realpath() C函数可以完成这个工作,但是我在命令行上没有看到任何可用的东西。 这是一个快速和肮脏的替代品:

 #!/bin/bash realpath() { [[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}" } realpath "$0" 

如果以/开头,则逐字打印path。 如果不是,它必须是一个相对path,所以它前面加上$PWD#./ part从$1的前面./

这三个简单的步骤将解决这个问题和许多其他的OSX问题:

  1. 安装自制软件
  2. brew install coreutils
  3. grealpath .

(3)可能会改变为实际realpath ,参见(2)输出

啊。 我发现以前的答案有一些原因,特别是它们不能解决多层次的符号链接,而且它们是非常“Bash-y”的。 尽pipe原始问题明确要求提供“Bash脚本”,但也提到了Mac OS X的类似BSD的非GNU readlink 。 所以这里是一个合理的可移植性的尝试(我已经用bash检查了这个“sh”和短划线),解决了任意数量的符号链接; 它也应该与path中的空白一起工作,虽然我不确定这个行为是否有空白的实用程序本身的基名,所以也许,呃,避免这种情况?

 #!/bin/sh realpath() { OURPWD=$PWD cd "$(dirname "$1")" LINK=$(readlink "$(basename "$1")") while [ "$LINK" ]; do cd "$(dirname "$LINK")" LINK=$(readlink "$(basename "$1")") done REALPATH="$PWD/$(basename "$1")" cd "$OURPWD" echo "$REALPATH" } realpath "$@" 

希望可以对某人有用。

我正在寻找在系统configuration脚本中使用的解决scheme,即在安装Homebrew之前运行。 缺乏一个适当的解决scheme,我只是把任务分stream到一个跨平台的语言,例如Perl:

 script_abspath=$(perl -e 'use Cwd "abs_path"; print abs_path(@ARGV[0])' -- "$0") 

更常见的是我们真正想要的是包含目录:

 here=$(perl -e 'use File::Basename; use Cwd "abs_path"; print dirname(abs_path(@ARGV[0]));' -- "$0") 

使用python来获取它

 #!/usr/bin/env python import os import sys print(os.path.realpath(sys.argv[1])) 

Python解决scheme更友好的命令行版本:

 python -c "import os; print(os.path.realpath('$1'))" 

那么作为替代呢? (忽略格式改变)所有带有path的文件(相对或绝对) – cd到文件的目录,并使用目录。 这将清理任何相对(即/tmp/../etc/passwd =>将显示为/etc/passwd
[没有拿这个,在多年前的某个地方见过]

 function abspath() { case "${1}" in [./]*) echo "$(cd ${1%/*}; pwd)/${1##*/}" ;; *) echo "${PWD}/${1}" ;; esac } 

正如你所看到的那样,大约在6个月前,我对此进行了一次尝试。 直到我发现自己再次需要类似的东西,我完全忘了它。 看到这是多么简单,我完全震惊了。 我一直在教自己编写相当密集的大约一年,但我常常觉得,如果事情处于最坏的状态,我可能还没学到任何东西。

我会删除上面的“解决scheme”,但是我真的很喜欢它是我在过去几个月中真正学到了多less东西的logging。

但是我离题了。 我坐下来,昨天晚上全部工作。 评论中的解释应该是足够的。 如果你想跟踪我继续工作的副本, 你可以按照这个要点。 这可能是你需要的。

 #!/bin/sh # dash bash ksh # !zsh (issues). G. Nixon, 12/2013. Public domain. ## 'linkread' or 'fullpath' or (you choose) is a little tool to recursively ## dereference symbolic links (ala 'readlink') until the originating file ## is found. This is effectively the same function provided in stdlib.h as ## 'realpath' and on the command line in GNU 'readlink -f'. ## Neither of these tools, however, are particularly accessible on the many ## systems that do not have the GNU implementation of readlink, nor ship ## with a system compiler (not to mention the requisite knowledge of C). ## This script is written with portability and (to the extent possible, speed) ## in mind, hence the use of printf for echo and case statements where they ## can be substituded for test, though I've had to scale back a bit on that. ## It is (to the best of my knowledge) written in standard POSIX shell, and ## has been tested with bash-as-bin-sh, dash, and ksh93. zsh seems to have ## issues with it, though I'm not sure why; so probably best to avoid for now. ## Particularly useful (in fact, the reason I wrote this) is the fact that ## it can be used within a shell script to find the path of the script itself. ## (I am sure the shell knows this already; but most likely for the sake of ## security it is not made readily available. The implementation of "$0" ## specificies that the $0 must be the location of **last** symbolic link in ## a chain, or wherever it resides in the path.) This can be used for some ## ...interesting things, like self-duplicating and self-modifiying scripts. ## Currently supported are three errors: whether the file specified exists ## (ala ENOENT), whether its target exists/is accessible; and the special ## case of when a sybolic link references itself "foo -> foo": a common error ## for beginners, since 'ln' does not produce an error if the order of link ## and target are reversed on the command line. (See POSIX signal ELOOP.) ## It would probably be rather simple to write to use this as a basis for ## a pure shell implementation of the 'symlinks' util included with Linux. ## As an aside, the amount of code below **completely** belies the amount ## effort it took to get this right -- but I guess that's coding for you. ##===-------------------------------------------------------------------===## for argv; do :; done # Last parameter on command line, for options parsing. ## Error messages. Use functions so that we can sub in when the error occurs. recurses(){ printf "Self-referential:\n\t$argv ->\n\t$argv\n" ;} dangling(){ printf "Broken symlink:\n\t$argv ->\n\t"$(readlink "$argv")"\n" ;} errnoent(){ printf "No such file: "$@"\n" ;} # Borrow a horrible signal name. # Probably best not to install as 'pathfull', if you can avoid it. pathfull(){ cd "$(dirname "$@")"; link="$(readlink "$(basename "$@")")" ## 'test and 'ls' report different status for bad symlinks, so we use this. if [ ! -e "$@" ]; then if $(ls -d "$@" 2>/dev/null) 2>/dev/null; then errnoent 1>&2; exit 1; elif [ ! -e "$@" -a "$link" = "$@" ]; then recurses 1>&2; exit 1; elif [ ! -e "$@" ] && [ ! -z "$link" ]; then dangling 1>&2; exit 1; fi fi ## Not a link, but there might be one in the path, so 'cd' and 'pwd'. if [ -z "$link" ]; then if [ "$(dirname "$@" | cut -c1)" = '/' ]; then printf "$@\n"; exit 0; else printf "$(pwd)/$(basename "$@")\n"; fi; exit 0 fi ## Walk the symlinks back to the origin. Calls itself recursivly as needed. while [ "$link" ]; do cd "$(dirname "$link")"; newlink="$(readlink "$(basename "$link")")" case "$newlink" in "$link") dangling 1>&2 && exit 1 ;; '') printf "$(pwd)/$(basename "$link")\n"; exit 0 ;; *) link="$newlink" && pathfull "$link" ;; esac done printf "$(pwd)/$(basename "$newlink")\n" } ## Demo. Install somewhere deep in the filesystem, then symlink somewhere ## else, symlink again (maybe with a different name) elsewhere, and link ## back into the directory you started in (or something.) The absolute path ## of the script will always be reported in the usage, along with "$0". if [ -z "$argv" ]; then scriptname="$(pathfull "$0")" # Yay ANSI l33t codes! Fancy. printf "\n\033[3mfrom/as: \033[4m$0\033[0m\n\n\033[1mUSAGE:\033[0m " printf "\033[4m$scriptname\033[24m [ link | file | dir ]\n\n " printf "Recursive readlink for the authoritative file, symlink after " printf "symlink.\n\n\n \033[4m$scriptname\033[24m\n\n " printf " From within an invocation of a script, locate the script's " printf "own file\n (no matter where it has been linked or " printf "from where it is being called).\n\n" else pathfull "$@" fi 

看看这个问题。 我find了更简洁的答案。

在path有空格的情况下获取脚本内的osx shell脚本所在的path

既然有别人指出的道路 ,

 // realpath.c #include <stdio.h> #include <stdlib.h> int main (int argc, char* argv[]) { if (argc > 1) { for (int argIter = 1; argIter < argc; ++argIter) { char *resolved_path_buffer = NULL; char *result = realpath(argv[argIter], resolved_path_buffer); puts(result); if (result != NULL) { free(result); } } } return 0; } 

Makefile文件:

 #Makefile OBJ = realpath.o %.o: %.c $(CC) -c -o $@ $< $(CFLAGS) realpath: $(OBJ) gcc -o $@ $^ $(CFLAGS) 

然后用make编译,并加上一个软链接:
ln -s $(pwd)/realpath /usr/local/bin/realpath