Tôi ghét phải thực hiện một triển khai khác, nhưng tôi cần a) triển khai vỏ di động, thuần túy và b) phạm vi kiểm tra đơn vị , vì số lượng các trường hợp cạnh cho một cái gì đó như thế này là không tầm thường .
Xem dự án của tôi trên Github để kiểm tra và mã đầy đủ. Dưới đây là tóm tắt của việc thực hiện:
Như Keith Smith khéo léo chỉ ra, readlink -f
thực hiện hai điều: 1) giải quyết đệ quy liên kết theo cách đệ quy và 2) chuẩn hóa kết quả, do đó:
realpath() {
canonicalize_path "$(resolve_symlinks "$1")"
}
Đầu tiên, việc thực hiện trình giải quyết 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
}
Lưu ý rằng đây là một phiên bản đơn giản hóa của việc thực hiện đầy đủ . Việc thực hiện đầy đủ thêm một kiểm tra nhỏ cho các chu kỳ liên kết tượng trưng , cũng như mát xa đầu ra một chút.
Cuối cùng, chức năng chuẩn hóa một đường dẫn:
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")
}
Đó là nó, ít nhiều. Đủ đơn giản để dán vào tập lệnh của bạn, nhưng đủ khó để bạn có thể điên khi dựa vào bất kỳ mã nào không có bài kiểm tra đơn vị cho các trường hợp sử dụng của bạn.
readlink
có thể là một lệnh dựng sẵn hoặc lệnh bên ngoài.