如何在MacOS上获取shell脚本的绝对路径名?

时间:2011-04-22 14:38:17

标签: macos bash shell sh

MacOS上不存在

readlink -f。我设法在网上找到的唯一可用的Mac OS解决方案是:

if [[ $(echo $0 | awk '/^\//') == $0 ]]; then
    ABSPATH=$(dirname $0)
else
    ABSPATH=$PWD/$(dirname $0)
fi

任何人都可以向这个看似微不足道的任务提出更优雅的建议吗?

11 个答案:

答案 0 :(得分:62)

另一个(也很丑陋)选项:

ABSPATH=$(cd "$(dirname "$0")"; pwd)

答案 1 :(得分:9)

获取shell脚本的绝对路径

从我的.bashrc挖出一些旧脚本,并稍微更新了语法,添加了一个测试套件。

支架

  • source ./script(由.点运算符调用时)
  • 绝对路径/路径/到/脚本
  • 相对路径,如./script
  • / path / dir1 /../ dir2 / dir3 /../ script
  • 从symlink调用时
  • 当符号链接嵌套时,例如foo->dir1/dir2/bar bar->./../doe doe->script
  • 当来电者更改脚本名称

它已在实际项目中经过测试并成功使用,但可能存在我不知道的极端情况 如果您能够找到这种情况,请告诉我 (首先,我知道这不会在sh shell上运行)

代码

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`]"

已知问题

脚本必须位于磁盘上,让它通过网络。 如果您尝试从PIPE运行此脚本,它将无法正常工作

wget -o /dev/null -O - http://host.domain/dir/script.sh |bash

从技术上讲,它是未定义的 实际上,没有理智的方法来检测这一点。

使用的测试用例

当前的测试用例,检查它是否有效。

#!/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

PurpleFox又名GreenFox

答案 2 :(得分:3)

使用bash我建议这种方法。首先cd到目录,然后使用pwd获取当前目录。之后,您必须返回旧目录,以确保您的脚本不会为调用它的其他脚本创建副作用。

cd "$(dirname -- "$0")"
dir="$PWD"
echo "$dir"
cd - > /dev/null

这种解决方案在复杂路径下是安全的。如果你把引号放在一边,你就永远不会有空格或特殊字符的麻烦。

注意:/ dev / null是require或“cd - ”打印返回的路径。

答案 3 :(得分:2)

另请注意,homebrew的{​​{3}})coreutils个套餐包含realpath/opt/local/bin中创建的链接)。

$ realpath bin
/Users/nhed/bin

答案 4 :(得分:1)

如果你不介意使用perl:

ABSPATH=$(perl -MCwd=realpath -e "print realpath '$0'")

答案 5 :(得分:1)

你能在剧本中尝试这样的东西吗?

echo $(pwd)/"$0"

在我的机器中显示:

/home/barun/codes/ns2/link_down/./test.sh

这是shell脚本的绝对路径名。

答案 6 :(得分:1)

我发现这对符号链接/动态链接很有用 - 仅适用于GNU readlink(因为-f标志):

# 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 "$0")")

# use $dirname to find a relative file
cat "$dirname"/foo/bar.txt

答案 7 :(得分:0)

这是我使用的,可能需要在这里或那里进行调整

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

这适用于任何文件 - 您可以将其作为abspath ${0}

调用

第一种情况通过cd-ing到路径来处理相对路径并让pwd弄明白

第二种情况是处理本地文件($ {1 ## /}不起作用)

这不会尝试撤消符号链接!

答案 8 :(得分:0)

只要它不是符号链接,它就可以工作,并且可能稍微不那么难看:

ABSPATH=$(dirname $(pwd -P $0)/${0#\.\/})

答案 9 :(得分:0)

如果您使用的是ksh,${.sh.file}参数将设置为脚本的绝对路径名。获取脚本的父目录:${.sh.file%/*}

答案 10 :(得分:0)

我使用下面的函数来模拟" readlink -f"对于必须在Linux和Mac OS X上运行的脚本。

#!/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=$1
    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 - ' "$1"
    read-link $1
    shift
done

这是一些常见的Mac OS X目标的输出:

$ ./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

这是一些linux目标的输出。

$ ./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