是否有一个命令来检索给定相对路径的绝对路径?
例如,我想要$line包含dir ./etc/中每个文件的绝对路径
find ./ -type f | while read line; do
echo $line
done
是否有一个命令来检索给定相对路径的绝对路径?
例如,我想要$line包含dir ./etc/中每个文件的绝对路径
find ./ -type f | while read line; do
echo $line
done
当前回答
基于@EugenKonkov的回答和@HashChange的回答,我的回答结合了前者的简洁和对。和. .关于后者。我相信下面所有的选项仅仅依赖于基本的Shell命令语言POSIX标准。
使用dirname和basename,一个选项是:
absPathDirname()
{
[ -d "${1}" ] && set -- "${1}" || set -- "`dirname "${1}"`" "/`basename "${1}"`"
echo "`cd "${1}"; pwd`${2}";
}
不使用dirname或basename,另一个简单的选项是:
absPathMinusD()
{
[ -d "${1}" ] && set -- "${1}" || set -- "${1%${1##*/}}" "/${1##*/}"
echo "`cd "${1:-.}"; pwd`${2}";
}
我会推荐上面两个选择中的一个,其他的只是为了好玩…
Grep版本:
absPathGrep()
{
echo "`[ "${1##/*}" ] && echo "$1" | grep -Eo '^(.*/)?\.\.($|/)' | { read d && cd "$d"; echo "${PWD}/${1#$d}"; } || echo "$1"`"
}
作为一个有趣的“可以用shell有限的RegEx做什么”的例子:
absPathShellReplace()
{
E="${1##*/}"; D="${E#$E${E#.}}"; DD="${D#$D${D#..}}"
DIR="${1%$E}${E#$DD}"; FILE="${1#$DIR}"; SEP=${FILE:+/}
echo "`cd "${DIR:-.}"; pwd`${SEP#$DIR}$FILE"
}
其他回答
我最喜欢的解决方案是@EugenKonkov的解决方案,因为它没有暗示其他实用程序的存在(coreutils包)。
但是对于相对路径“.”和“..”它失败了,所以这里有一个稍微改进的版本来处理这些特殊情况。
但是,如果用户没有cd到相对路径的父目录的权限,它仍然会失败。
#! /bin/sh
# Takes a path argument and returns it as an absolute path.
# No-op if the path is already absolute.
function to-abs-path {
local target="$1"
if [ "$target" == "." ]; then
echo "$(pwd)"
elif [ "$target" == ".." ]; then
echo "$(dirname "$(pwd)")"
else
echo "$(cd "$(dirname "$1")"; pwd)/$(basename "$1")"
fi
}
我无法找到一个解决方案,可以在Mac OS Catalina, Ubuntu 16和Centos 7之间巧妙地移植,所以我决定用python内联来做,它对我的bash脚本工作得很好。
to_abs_path() {
python -c "import os; print os.path.abspath('$1')"
}
to_abs_path "/some_path/../secrets"
下面是一个相当短的函数,可以用来完全绝对化和规范化任何给定的输入路径,只使用POSIX shell和readlink语义:
canonicalize_path() {
(
FILEPATH="$1"
for _ in 1 2 3 4 5 6 7 8; # Maximum symlink recursion depth
do
cd -L "`case "${FILEPATH}" in */*) echo "${FILEPATH%/*}";; *) echo ".";; esac`/" # cd $(dirname)
if ! FILEPATH="$(readlink "${FILEPATH##*/}" || ( echo "${FILEPATH##*/}" && false ) )";
then
break
fi
done
cd -P "." || return $?
echo "$(pwd)/${FILEPATH}"
)
}
如果引用的文件不存在,则只解析指向最终文件名的目录路径。如果指向该文件路径的任何目录都不存在,将返回一个cd错误。这恰好是它试图模拟的GNU/Linux readlink -f命令的确切语义。
在bash/zsh中,您还可以将数字列表压缩为{1..8}或类似的。之所以选择8这个数字,是因为这是Linux多年来的最大限制,后来在4.2版本中将整个路径的分辨率限制为40。如果达到了分辨率限制,代码不会失败,而是返回最后考虑的路径——但是可以添加显式的[- l "${FILEPATH}"]检查来检测这种情况。
这段代码也可以很容易地重用,以确保当前工作目录与已执行脚本在文件系统中的位置匹配(shell脚本的常见需求),只需删除函数和子shell包装器:
FILEPATH="$0"
for _ in 1 2 3 4 5 6 7 8; # Maximum symlink recursion depth
do
cd -L "`case "${FILEPATH}" in */*) echo "${FILEPATH%/*}";; *) echo ".";; esac`/" # cd $(dirname)
if ! FILEPATH="$(readlink "${FILEPATH##*/}" || ( echo "${FILEPATH##*/}" && false ) )";
then
break
fi
done
cd -P "."
FILEPATH="$(pwd)/${FILEPATH}"
use:
find "$(pwd)"/ -type f
获取所有文件或
echo "$(pwd)/$line"
显示完整路径(如果相对路径关系到)
对@ernest-a相当不错的版本的改进:
absolute_path() {
cd "$(dirname "$1")"
case $(basename $1) in
..) echo "$(dirname $(pwd))";;
.) echo "$(pwd)";;
*) echo "$(pwd)/$(basename $1)";;
esac
}
这正确地处理了路径的最后一个元素是..,在这种情况下,@ernest-a的答案中的“$(pwd)/$(basename“$1”)”将通过作为accurate_sub_path/spurious_subdirectory/…