如果您的脚本需要存在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
主要用于将消息打印到标准输出(stdout)中,但很多脚本编写者依赖的echo
行为实际上没有被规定。即使是著名的-n
也没有被标准化,或者使用\t
表示制表符也没有被标准化。POSIX标准说:
要写入标准输出的字符串。如果第一个操作数是-n,或者任何操作数包含字符,结果由实现定义。
- https://pubs.opengroup.org/onlinepubs/9699919799/utilities/echo.html
因此,每当您想要将某些内容写入stdout并且它不是为了向用户打印消息时,建议使用printf
,因为printf
的行为完全被定义。我的函数使用stdout传递结果,这不是用户的消息,因此只有使用printf
才能保证完美的可移植性。