如何在bash中获取文件的绝对目录?


81

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

我希望脚本转到包含输入文件的文件夹,以执行更多命令。

那么,如何从输入文件中获取文件夹(以及仅文件夹)?(在Linux中。)


您是要提供输入文件的完整路径,还是只是相对于当前工作目录的路径?
dmh

dhm:输入给出相对路径,我要绝对路径减去文件名。
BobMcGee

Answers:


148

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

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


17
注意 readlink -f,不幸的这在OS X上不起作用。
埃米尔(Emil Sit)2013年

我不需要整个路径,我只想要文件夹。但这足以提供帮助:readlink -f relativeFileLocation | xargs目录名
BobMcGee

1
@EmilSit:您是对的-本机。但是你可以brew install coreutils得到它。 stackoverflow.com/a/4031502/1022967
mpettis '16

3
请不要建议使用${1%/*}${1##*/}替代dirnamebasename。如果给它一个简单的文件名dirname foo.bar,则第一个会中断(正确地是.),第二个会中断以斜杠结尾的目录(basename /foo/bar/会正确地是bar)。
卡米洛·马丁

1
@ChenLevy为了更快(我认为basename/dirname不是内置的),有些人可能会倾向于使用它,他们会首先看到它起作用,但是在将来某个时候突然中断,看起来似乎没有任何好处。原因。我会避免建议它作为一种选择,或者会否认它并不是真正的防弹手段。
卡米洛·马丁

18

看一下的手册页realpath,我用它和类似的东西:

CONTAININGDIR = $(真实路径$ {FILEPATH%/ *})

做听起来像是您要尝试做的事情。


realpath解决了相对目录的符号链接对我来说很奇怪
Erik Aronesty

谢谢!在Mac OS上为我工作
Dmitry Klochkov 2016年

6

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

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

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

一些解释:

  1. 该脚本获取相对路径作为参数 "$1"
  2. 然后我们得到目录名该路径的部分(您可以将目录或文件传递给此脚本):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

我一直在使用readlink -f在Linux上工作

所以

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

PWD=$(pwd)

cd $DIR

#<do more work>

cd $PWD

5
不要在Bash中使用大写的变量名,有一天它会与已经存在的变量发生冲突……哦,现在是今天:您是否知道PWDBash中已经存在该变量,并且其值实际上是当前目录?并使用更多的报价。
gniourf_gniourf
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.