(zsh)スクリプトがその絶対パスを決定するための移植可能な方法は何ですか?
Linuxでは私は次のようなものを使用します
mypath=$(readlink -f $0)
...しかし、これはポータブルではありません。 (たとえば、darwinのreadlink
は-f
フラグを認識せず、同等のフラグもありません。)(また、これにreadlink
を使用することは、確かに、非常にあいまいなハックです。)
よりポータブルな方法は何ですか?
zsh
を使用すると、次のようになります。
_mypath=$0:A
_
他のシェルでは、realpath()
とreadlink()
は標準関数です(後者はシステムコールです)、realpath
とreadlink
は標準コマンドではありませんただし、一部のシステムには、さまざまな動作と機能セットを備えたどちらか一方または両方があります。
多くの場合、移植性のために、Perl
に頼ることができます:
_abs_path() {
Perl -MCwd -le '
for (@ARGV) {
if ($p = Cwd::abs_path $_) {
print $p;
} else {
warn "abs_path: $_: $!\n";
$ret = 1;
}
}
exit $ret' "$@"
}
_
これは、dirnameが存在する限りファイルが存在しない場合でも文句を言わないという点で、realpath()
(GNU _readlink -f
_)よりもGNUの_readlink -e
_のように動作します。
Zshでは、次のことができます。
mypath=${0:a}
または、スクリプトが存在するディレクトリを取得するには:
mydir=${0:a:h}
出典:zshexpn(1)manページ、セクションHISTORY EXPANSION、サブセクションModifiers(または単にinfo -f zsh -n Modifiers
)。
私はこれを数年間使用しています:
# The absolute, canonical ( no ".." ) path to this script
canonical=$(cd -P -- "$(dirname -- "$0")" && printf '%s\n' "$(pwd -P)/$(basename -- "$0")")
この構文は、Bourne Shellスタイルのインタープリターに移植可能である必要があります(bash
、ksh88
、ksh93
、zsh
、mksh
、dash
およびbusybox sh
):
mypath=$(exec 2>/dev/null;cd -- $(dirname "$0"); unset PWD; /usr/bin/pwd || /bin/pwd || pwd)
echo mypath=$mypath
このバージョンは、レガシーAT&T Bourne Shell(非POSIX)に互換性を追加します:
mypath=`dirname "$0"`
mypath=`exec 2>/dev/null;(cd -- "$mypath") && cd -- "$mypath"|| cd "$mypath"; unset PWD; /usr/bin/pwd || /bin/pwd || pwd`
echo mypath=$mypath
あなたが本当に絶対パス、つまりルートディレクトリからのパスを意味すると仮定します:
case $0 in
/*) mypath=$0;;
*) mypath=$PWD/$0;;
esac
ちなみに、これはどのBourneスタイルのシェルでも機能します。
すべてのシンボリックリンクが解決されたパスを意味する場合、それは別の問題です。 readlink -f
は、Linux(一部の簡略化されたBusyBoxシステムを除く)、FreeBSD、NetBSD、OpenBSD、Cygwinでは機能しますが、OS/X、AIX、HP/UX、またはSolarisでは機能しません。 readlink
がある場合は、ループで呼び出すことができます。
realpath () {
[ -e "$1" ] || return
case $1 in
/*) :;;
*) set "$PWD/$1";;
esac
while [ -L "$1" ]; do
set "${1%/*}" "$(readlink "$1")"
case $2 in
/*) set "$2";;
*) if [ -z "$1" ]; then set "/$2"; else set "$(cd "$1" && pwd -P)/$2"; fi;;
esac
done
case $1 in
*/.|*/..) set "$(cd "$1" && pwd -P)";;
*/./*|*/../*) set "$(cd "${1%/*}" && pwd -P)/${1##*/}"
esac
realpath=$1
}
readlink
がない場合は、ls -n
で概算できますが、これは、ls
がファイル名に印刷できない文字を変換しない場合にのみ機能します。
poor_mans_readlink () {
if [ -L "$1" ]; then
set -- "$1" "$(LC_ALL=C command ls -n -- "$2"; echo z)"
set -- "${2%??}"
set -- "${2#*"$1 -> "}"
fi
printf '%s\n' "$1"
}
(余分なz
は、リンクターゲットが改行で終わっている場合に使用します。そうしないと、コマンドの置換によって食い尽くされてしまいます。ところで、realpath
関数は、ディレクトリ名の大文字小文字を処理しません。 )
アルゴリズムの再定義を防ぐためにpythonが利用可能な場合、交感神経のワンライナーはどうですか?
function readlink { python -c "import os.path; print os.path.realpath('$1')"; }