MacOSでシェルスクリプトの絶対パス名を取得する方法
readlink -f
はMacOSには存在しません。ネットで見つけたMac OSの唯一の実用的なソリューションは次のとおりです。
if [[ $(echo $0 | awk '/^\//') == $0 ]]; then
ABSPATH=$(dirname $0)
else
ABSPATH=$PWD/$(dirname $0)
fi
誰もがこの一見ささいな仕事にもっとエレガントなことを提案できますか?
別の(どちらかと言えば醜い)オプション:
ABSPATH=$(cd "$(dirname "$0")"; pwd)
シェルスクリプトの絶対パスを取得する
私の.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
Bashを使用して、このアプローチをお勧めします。最初にディレクトリにcdし、次にpwdを使用して現在のディレクトリを取得します。その後、古いディレクトリに戻って、スクリプトがそれを呼び出す他のスクリプトに副作用を引き起こさないようにする必要があります。
cd "$(dirname -- "$0")"
dir="$PWD"
echo "$dir"
cd - > /dev/null
このソリューションは、複雑なパスでも安全です。引用符を付けると、スペースや特殊文字に問題が発生することはありません。
注:/ dev/nullはrequireまたは "cd-"で戻り先のパスを出力します。
homebrew
の( http://brew.sh )coreutils
パッケージにはrealpath
が含まれていることにも注意してください(リンクが作成された/opt/local/bin
)。
$ realpath bin
/Users/nhed/bin
スクリプト内でこのようなことを試すことができますか?
echo $(pwd)/"$0"
私のマシンではそれは示しています:
/home/barun/codes/ns2/link_down/./test.sh
これは、シェルスクリプトの絶対パス名です。
Perlを使用してもかまわない場合:
ABSPATH = $(Perl -MCwd = realpath -e "print realpath '$ 0'")
これはシンボリックリンク/ダイナミックリンクに役立つことがわかりました-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
以下の関数を使用して、LinuxとMac OS Xの両方で実行する必要があるスクリプトの「readlink -f」をエミュレートします。
#!/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
これはシンボリックリンクでない限り機能し、おそらく少し醜くないでしょう:
ABSPATH=$(dirname $(pwd -P $0)/${0#\.\/})
Kshを使用している場合、${.sh.file}
パラメータは、スクリプトの絶対パス名に設定されます。スクリプトの親ディレクトリを取得するには:${.sh.file%/*}
これは私が使っているもので、あちこちでTweakが必要かもしれません
abspath ()
{
case "${1}" in
[./]*)
local ABSPATH="$(cd ${1%/*}; pwd)/${1##*/}"
echo "${ABSPATH/\/\///}"
;;
*)
echo "${PWD}/${1}"
;;
esac
}
これはどのファイルにも当てはまります-呪いをかけるだけでabspath ${0}
として呼び出すことができます
最初のケースは、パスにcd-ingし、pwdにそれを理解させることによって相対パスを扱います
2番目のケースは、ローカルファイルを処理する場合です($ {1 ## /}は機能しませんでした)。
これはシンボリックリンクを元に戻すことを試みません!