web-dev-qa-db-ja.com

MacOSでシェルスクリプトの絶対パス名を取得する方法

readlink -fはMacOSには存在しません。ネットで見つけたMac OSの唯一の実用的なソリューションは次のとおりです。

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

誰もがこの一見ささいな仕事にもっとエレガントなことを提案できますか?

30
Ivan Balashov

別の(どちらかと言えば醜い)オプション:

ABSPATH=$(cd "$(dirname "$0")"; pwd)
63
Gordon Davisson

シェルスクリプトの絶対パスを取得する

私の.bashrcからいくつかの古いスクリプトを掘り出し、構文を少し更新し、テストスイートを追加しました。

サポート

  • ソース./script(.ドット演算子によって呼び出された場合)
  • 絶対パス/ path/to/script
  • ./scriptのような相対パス
  • /path/dir1/../dir2/dir3/../script
  • Symlinkから呼び出されたとき
  • シンボリックリンクがネストされている場合。例)foo->dir1/dir2/bar bar->./../doe doe->script
  • 呼び出し元がスクリプト名を変更したとき

これはテストされ、実際のプロジェクトで使用されて成功していますが、私が知らないコーナーケースがあるかもしれません。
このような状況を見つけた場合は、お知らせください。
(1つには、これはshシェルでは実行されないことを知っています)

コード

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

9
GreenFox

Bashを使用して、このアプローチをお勧めします。最初にディレクトリにcdし、次にpwdを使用して現在のディレクトリを取得します。その後、古いディレクトリに戻って、スクリプトがそれを呼び出す他のスクリプトに副作用を引き起こさないようにする必要があります。

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

このソリューションは、複雑なパスでも安全です。引用符を付けると、スペースや特殊文字に問題が発生することはありません。

注:/ dev/nullはrequireまたは "cd-"で戻り先のパスを出力します。

3
Lynch

homebrewの( http://brew.shcoreutilsパッケージにはrealpathが含まれていることにも注意してください(リンクが作成された/opt/local/bin)。

$ realpath bin
/Users/nhed/bin
3
nhed

スクリプト内でこのようなことを試すことができますか?

echo $(pwd)/"$0"

私のマシンではそれは示しています:

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

これは、シェルスクリプトの絶対パス名です。

1
Barun

Perlを使用してもかまわない場合:

 ABSPATH = $(Perl -MCwd = realpath -e "print realpath '$ 0'")
1
William Pursell

これはシンボリックリンク/ダイナミックリンクに役立つことがわかりました-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
1
Yoshua Wuyts

以下の関数を使用して、LinuxとMac OS Xの両方で実行する必要があるスクリプトの「readlink -f」をエミュレートします。

#!/bin/bash
# This was re-worked on 2018-10-26 after [email protected] 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
0
Joe Linoff

これはシンボリックリンクでない限り機能し、おそらく少し醜くないでしょう:

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

Kshを使用している場合、${.sh.file}パラメータは、スクリプトの絶対パス名に設定されます。スクリプトの親ディレクトリを取得するには:${.sh.file%/*}

0
Nathan Weeks

これは私が使っているもので、あちこちでTweakが必要かもしれません

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

これはどのファイルにも当てはまります-呪いをかけるだけでabspath ${0}として呼び出すことができます

最初のケースは、パスにcd-ingし、pwdにそれを理解させることによって相対パスを扱います

2番目のケースは、ローカルファイルを処理する場合です($ {1 ## /}は機能しませんでした)。

これはシンボリックリンクを元に戻すことを試みません!

0
nhed