如何获取相对路径的绝对路径

241

有没有一条命令可以根据相对路径获取绝对路径?

例如,我想让$line包含dir目录下每个文件的绝对路径:./etc/

find ./ -type f | while read line; do
   echo $line
done

8
可能是之前讨论的问题的重复:将相对路径转换为绝对路径,或者这个 - Dennis Williamson
1
可能是重复的问题:bash/fish命令打印文件的绝对路径 - Trevor Boyd Smith
迄今为止比列出的任何解决方案都要好得多的解决方案在这里:如何在Unix中将相对路径转换为绝对路径 - Daniel Genin
你可能想看看这个链接,因为它可以帮助你在Git仓库中相对于存储库路径配置脚本的路径。 - Nae
26个回答

1
echo "mydir/doc/ mydir/usoe ./mydir/usm" |  awk '{ split($0,array," "); for(i in array){ system("cd "array[i]" && echo $PWD") } }'

在上面的片段中,awk用于分割从echo(通过管道)接收到的目录字符串并创建数组。
以下是正在发生的步骤的逐步列表:
- 用空格分隔的目录列表被导向awk - awk通过空格将管道文本拆分为数组 - awk循环遍历数组中的每个项(变量i) - awk使用awk系统命令运行cli命令 - CLI命令更改目录(cd)到数组中指定的路径“i”,然后使用环境变量$PWD打印(echo)工作目录
使用CD来浏览每个目录,然后打印$PWD的值是让操作系统确定绝对路径的好方法。

3
感谢您提供的代码片段,它可能会为一些短期帮助提供有限的帮助。一个适当的解释将极大地提高其长期价值,因为它可以展示为什么这是一个好的解决方案,并使它对未来读者在其他类似问题上更加有用。请编辑您的答案添加一些解释,包括您所做出的假设。 - Toby Speight

1
这是一个相当简短的函数,可使用仅限于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的最大限制为8,然后在版本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}"

1
您可以使用bash字符串替换来处理任何相对路径$line:
line=$(echo ${line/#..\//`cd ..; pwd`\/})
line=$(echo ${line/#.\//`pwd`\/})
echo $line

基本的前缀替换遵循以下公式:
${string/#substring/replacement}
这里对它进行了详细讨论:https://www.tldp.org/LDP/abs/html/string-manipulation.html
当我们想要将/作为字符串的一部分查找/替换时,\字符会将其否定掉。

请注意,我认为您可以只使用 dirnamebasename。类似这样的语句:dir=\dirname $line`; file=`basename $line`; line=`cd $dir; pwd`/$file` - Alexis Wilke

0

基于这个答案,作者为@EugenKonkov这个答案,作者为@HashChange。我的答案结合了前者的简洁性和后者对 . .. 的处理。我相信以下所有选项都不依赖于除基本Shell命令语言POSIX标准之外的任何内容。

使用dirnamebasename,一个选项是:

absPathDirname()
{
    [ -d "${1}" ] && set -- "${1}" || set -- "`dirname "${1}"`" "/`basename "${1}"`"
    echo "`cd "${1}"; pwd`${2}";
}

不使用dirnamebasename,另一个简短的选项是:

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有限的正则表达式可以做什么”的有趣示例:
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"
}

0

这是一个链接解决方案,例如,当realpath失败时,无论是因为它未安装还是因为它退出错误代码,都会尝试下一个解决方案,直到找到正确的路径。

#!/bin/bash

function getabsolutepath() {
    local target;
    local changedir;
    local basedir;
    local firstattempt;

    target="${1}";
    if [ "$target" == "." ];
    then
        printf "%s" "$(pwd)";

    elif [ "$target" == ".." ];
    then
        printf "%s" "$(dirname "$(pwd)")";

    else
        changedir="$(dirname "${target}")" && basedir="$(basename "${target}")" && firstattempt="$(cd "${changedir}" && pwd)" && printf "%s/%s" "${firstattempt}" "${basedir}" && return 0;
        firstattempt="$(readlink -f "${target}")" && printf "%s" "${firstattempt}" && return 0;
        firstattempt="$(realpath "${target}")" && printf "%s" "${firstattempt}" && return 0;

        # If everything fails... TRHOW PYTHON ON IT!!!
        local fullpath;
        local pythoninterpreter;
        local pythonexecutables;
        local pythonlocations;

        pythoninterpreter="python";
        declare -a pythonlocations=("/usr/bin" "/bin");
        declare -a pythonexecutables=("python" "python2" "python3");

        for path in "${pythonlocations[@]}";
        do
            for executable in "${pythonexecutables[@]}";
            do
                fullpath="${path}/${executable}";

                if [[ -f "${fullpath}" ]];
                then
                    # printf "Found ${fullpath}\\n";
                    pythoninterpreter="${fullpath}";
                    break;
                fi;
            done;

            if [[ "${pythoninterpreter}" != "python" ]];
            then
                # printf "Breaking... ${pythoninterpreter}\\n"
                break;
            fi;
        done;

        firstattempt="$(${pythoninterpreter} -c "import os, sys; print( os.path.abspath( sys.argv[1] ) );" "${target}")" && printf "%s" "${firstattempt}" && return 0;
        # printf "Error: Could not determine the absolute path!\\n";
        return 1;
    fi
}

printf "\\nResults:\\n%s\\nExit: %s\\n" "$(getabsolutepath "./asdfasdf/ asdfasdf")" "${?}"

0
如果您想将包含相对路径的变量转换为绝对路径,可以使用以下代码:
   dir=`cd "$dir"`

"cd" 命令在子 shell 中执行,因此只是回显当前工作目录,而不会改变实际的工作目录。


2
在bash-4.3-p46上,这个不起作用:当我运行dir=\cd ".."` && echo $dir`时,shell会打印一个空行。 - Michael

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接