如果您的脚本可能坚持存在 bash 或 bash 兼容的 shell,则 Alexander Klimetschek 的答案是可以的。它不适用于仅符合 POSIX 的 shell。
此外,当最终文件是根目录下的文件时,输出将是//file
,这在技术上并非不正确(/
系统将双精度视为单精度),但看起来很奇怪。
这是一个适用于所有符合 POSIX 标准的 shell 的版本,它使用的所有外部工具也是 POSIX 标准所要求的,并且它明确处理根文件的情况:
#!/bin/sh
abspath ( ) {
if [ ! -e "$1" ]; then
return 1
fi
file=""
dir="$1"
if [ ! -d "$dir" ]; then
file=$(basename "$dir")
dir=$(dirname "$dir")
fi
case "$dir" in
/*) ;;
*) dir="$(pwd)/$dir"
esac
result=$(cd "$dir" && pwd)
if [ -n "$file" ]; then
case "$result" in
*/) ;;
*) result="$result/"
esac
result="$result$file"
fi
printf "%s\n" "$result"
}
abspath "$1"
将其放入文件并使其可执行,您就有了一个 CLI 工具来快速获取文件和目录的绝对路径。或者只是复制该函数并在您自己的符合 POSIX 的脚本中使用它。它将相对路径转换为绝对路径并按原样返回绝对路径。
有趣的修改:
如果将该行替换为result=$(cd "$dir" && pwd)
,result=$(cd "$dir" && pwd -P)
则最终文件路径中的所有符号链接也会被解析。
如果您对第一次修改不感兴趣,您可以通过提前返回来优化绝对情况:
abspath ( ) {
if [ ! -e "$1" ]; then
return 1
fi
case "$1" in
/*)
printf "%s\n" "$1"
return 0
esac
file=""
dir="$1"
if [ ! -d "$dir" ]; then
file=$(basename "$dir")
dir=$(dirname "$dir")
fi
result=$(cd "$dir" && pwd)
if [ -n "$file" ]; then
case "$result" in
*/) ;;
*) result="$result/"
esac
result="$result$file"
fi
printf "%s\n" "$result"
}
既然问题会出现:为什么printf
而不是echo
?
echo
主要用于将用户的消息打印到标准输出。echo
脚本编写者所依赖的许多行为实际上是未指定的。即使是著名的也不是标准化的或for tab-n
的用法。\t
POSIX 标准说:
要写入标准输出的字符串。如果第一个操作数是 -n,或者任何操作数包含字符,则结果是实现定义的。
- https://pubs.opengroup.org/onlinepubs/9699919799/utilities/echo.html
因此,每当您想向标准输出写入内容并且不是为了向用户打印消息时,建议使用精确定义printf
的行为。printf
我的函数使用标准输出来传递结果,这不是给用户的消息,因此只使用printf
保证完美的可移植性。