如何在Bash中获取文件的绝对路径?

103

我编写了一个Bash脚本,该脚本以输入文件作为参数并读取它。
该文件包含一些相对于其位置的路径,指向其他文件。

我希望该脚本能够进入包含输入文件的文件夹,以执行后续命令。

在Linux中,如何从输入文件获取文件夹(仅文件夹)?


你是提供输入文件的完整路径还是相对于当前工作目录的路径? - dmh
dhm:输入给出相对路径,我想要减去文件名的绝对路径。 - BobMcGee
7个回答

179

要获取完整路径,请使用:

readlink -f relative/path/to/file

获取文件的目录:

dirname relative/path/to/file
你也可以将这两个组合起来:
dirname $(readlink -f relative/path/to/file)

如果您的系统上没有可用的readlink -f,您可以使用这个*:

function myreadlink() {
  (
  cd "$(dirname $1)"         # or  cd "${1%/*}"
  echo "$PWD/$(basename $1)" # or  echo "$PWD/${1##*/}"
  )
}

请注意,如果您只需要移动到指定为相对路径的文件所在的目录,则不需要知道绝对路径,相对路径是完全合法的,因此只需使用:

cd $(dirname relative/path/to/file)

如果你希望在脚本运行时返回到原始路径,请使用pushd而不是cd,并在完成后使用popd


* 尽管上面的myreadlink在这个问题的背景下已经足够好了,但它相对于建议使用的readlink工具存在一些限制。例如,它无法正确地跟随到具有不同basename的文件的链接。


18
请注意,不幸的是,readlink -f 在 OS X 上无法使用。 - Emil Sit
我不需要整个路径,只需要文件夹。但是这个命令足以有所帮助:readlink -f relativeFileLocation | xargs dirname - BobMcGee
2
@EmilSit:你说得对,但是你可以通过brew install coreutils来获取它。https://dev59.com/UnNA5IYBdhLWcg3wL62x#4031502。 - mpettis
3
请勿建议使用 ${1%/*}${1##*/} 作为 dirnamebasename 的替代品。前者对于简单文件名会出错(例如,dirname foo.bar 应该返回 .),而后者对于以斜杆结尾的目录也会出错(例如,basename /foo/bar/ 应该返回 bar)。 - Camilo Martin
1
@ChenLevy 有些人可能会倾向于使用它来提高速度(我认为basename/dirname不是内置函数),他们一开始会看到它正常工作,但在未来某个时刻突然崩溃,原因似乎没有什么好的理由。我建议避免将其作为选择,或者声明它并不是一个完全可靠的替代品。 - Camilo Martin
显示剩余6条评论

26

请看一下GNU/LinuxFreeBSDNetBSD上可用的realpath命令,但在OpenBSD 6.8上不可用。我使用类似以下的命令:

CONTAININGDIR=$(realpath ${FILEPATH%/*})

做你似乎试图做的事情。


realpath 对我来说有点奇怪,它会解析相对目录的符号链接。 - Erik Aronesty
谢谢!在我的Mac OS上有效。 - Dmitry Klochkov

6

这将适用于文件和文件夹:

absPath(){
    if [[ -d "$1" ]]; then
        cd "$1"
        echo "$(pwd -P)"
    else 
        cd "$(dirname "$1")"
        echo "$(pwd -P)/$(basename "$1")"
    fi
}

6
$cat abs.sh
#!/bin/bash
echo "$(cd "$(dirname "$1")"; pwd -P)"

一些解释:

  1. 这个脚本以相对路径作为参数 "$1"
  2. 然后我们获取该路径的 dirname 部分(您可以将目录或文件传递给此脚本):dirname "$1"
  3. 然后我们cd "$(dirname "$1");进入这个相对目录
  4. pwd -P并获取绝对路径。 -P选项将避免符号链接
  5. 最后一步是使用echo输出它

然后运行您的脚本:

abs.sh your_file.txt

1

试试我们在GitHub上免费提供给社区使用的全新Bash库产品realpath-lib。它干净、简单而且文档详尽,非常适合学习。你可以进行以下操作:

get_realpath <absolute|relative|symlink|local file path>

这个函数是该库的核心:
if [[ -f "$1" ]]
then
    # file *must* exist
    if cd "$(echo "${1%/*}")" &>/dev/null
    then
        # file *may* not be local
        # exception is ./file.ext
        # try 'cd .; cd -;' *works!*
        local tmppwd="$PWD"
        cd - &>/dev/null
    else
        # file *must* be local
        local tmppwd="$PWD"
    fi
else
    # file *cannot* exist
    return 1 # failure
fi

# reassemble realpath
echo "$tmppwd"/"${1##*/}"
return 0 # success

}

它是Bash 4+,不需要任何依赖,并且还提供了get_dirname、get_filename、get_stemname和validate_path。


0

以上答案的问题出在输入带有 "./" 的文件,例如 "./my-file.txt"

解决方法(众多之一):

    myfile="./somefile.txt"
    FOLDER="$(dirname $(readlink -f "${ARG}"))"
    echo ${FOLDER}

-1

我一直在使用在Linux上可用的readlink -f命令。

所以

FULL_PATH=$(readlink -f filename)
DIR=$(dirname $FULL_PATH)

PWD=$(pwd)

cd $DIR

#<do more work>

cd $PWD

5
不要在Bash中使用大写的变量名,因为它们有一天会与已存在的变量冲突......噢,而这一天现在已经到来了:你知道变量 PWD 在Bash中已经存在,并且它的值实际上是当前目录吗?另外,请多使用引号。 - gniourf_gniourf

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