私はさらに別の実装を重ねるのが嫌いですが、このようなもののエッジケースの数は重要ではないので、a)移植可能な純粋なシェル実装、およびb)ユニットテストカバレッジが必要でした。
テストと完全なコードについては、Githubで私のプロジェクトを参照してください。次に、実装の概要を示します。
キース・スミスが鋭敏に指摘するように、readlink -f
2つのことを行います。1)シンボリックリンクを再帰的に解決し、2)結果を正規化します。
realpath() {
canonicalize_path "$(resolve_symlinks "$1")"
}
最初に、symlinkリゾルバーの実装:
resolve_symlinks() {
local dir_context path
path=$(readlink -- "$1")
if [ $? -eq 0 ]; then
dir_context=$(dirname -- "$1")
resolve_symlinks "$(_prepend_path_if_relative "$dir_context" "$path")"
else
printf '%s\n' "$1"
fi
}
_prepend_path_if_relative() {
case "$2" in
/* ) printf '%s\n' "$2" ;;
* ) printf '%s\n' "$1/$2" ;;
esac
}
これは完全実装のわずかに簡略化されたバージョンであることに注意してください。完全な実装は、シンボリックリンクの小さなチェックを追加し、出力を少しマッサージします。
最後に、パスを正規化する関数:
canonicalize_path() {
if [ -d "$1" ]; then
_canonicalize_dir_path "$1"
else
_canonicalize_file_path "$1"
fi
}
_canonicalize_dir_path() {
(cd "$1" 2>/dev/null && pwd -P)
}
_canonicalize_file_path() {
local dir file
dir=$(dirname -- "$1")
file=$(basename -- "$1")
(cd "$dir" 2>/dev/null && printf '%s/%s\n' "$(pwd -P)" "$file")
}
それで、多かれ少なかれそれです。スクリプトに貼り付けるのに十分なほど単純ですが、ユースケース用の単体テストを持たないコードに依存するのに夢中になるほどトリッキーです。
readlink
組み込みコマンドまたは外部コマンドになる可能性があるため、これは重要です。