577

问题:是否有一个简单的 sh/bash/zsh/fish/... 命令来打印我提供的任何文件的绝对路径?

用例:我在目录中/a/b,我想c在命令行上打印文件的完整路径,以便我可以轻松地将其粘贴到另一个程序中:/a/b/c. 在处理长路径时,简单但一个小程序可能会为我节省 5 秒左右的时间,最终加起来。所以令我惊讶的是,我找不到一个标准的实用程序来做到这一点——真的没有吗?

这是一个示例实现,abspath.py:

#!/usr/bin/python
# Author: Diggory Hardy <diggory.hardy@gmail.com>
# Licence: public domain
# Purpose: print the absolute path of all input paths

import sys
import os.path
if len(sys.argv)>1:
    for i in range(1,len(sys.argv)):
        print os.path.abspath( sys.argv[i] )
    sys.exit(0)
else:
    print >> sys.stderr, "Usage: ",sys.argv[0]," PATH."
    sys.exit(1)
4

22 回答 22

774

采用realpath

$ realpath example.txt
/home/username/example.txt
于 2010-10-12T13:16:11.753 回答
348

试试readlink这将解决符号链接:

readlink -e /foo/bar/baz
于 2010-10-12T14:34:06.237 回答
297
#! /bin/sh
echo "$(cd "$(dirname "$1")"; pwd -P)/$(basename "$1")"
于 2010-10-12T13:51:38.627 回答
108

忘记哪些可能会readlinkrealpath可能不会安装在您的系统上。

在这里扩展dogbane 的答案,它表示为一个函数:

#!/bin/bash
get_abs_filename() {
  # $1 : relative filename
  echo "$(cd "$(dirname "$1")" && pwd)/$(basename "$1")"
}

然后你可以像这样使用它:

myabsfile=$(get_abs_filename "../../foo/bar/file.txt")

它如何以及为什么起作用?

该解决方案利用了 Bash 内置pwd命令在不带参数调用时将打印当前目录的绝对路径这一事实。

为什么我喜欢这个解决方案?

readlink它是可移植的,既不需要,也不需要realpath在给定 Linux/Unix 发行版的默认安装中通常不存在。

如果目录不存在怎么办?

如上所述,如果给定的目录路径不存在,该函数将失败并在 stderr 上打印。这可能不是你想要的。您可以扩展函数来处理这种情况:

#!/bin/bash
get_abs_filename() {
  # $1 : relative filename
  if [ -d "$(dirname "$1")" ]; then
    echo "$(cd "$(dirname "$1")" && pwd)/$(basename "$1")"
  fi
}

现在,如果父目录不存在,它将返回一个空字符串。

你如何处理尾随'..'或'.' 在输入?

好吧,在这种情况下,它确实给出了绝对路径,但不是最小路径。它看起来像:

/Users/bob/Documents/..

如果您想解决“..”,您需要制作如下脚本:

get_abs_filename() {
  # $1 : relative filename
  filename=$1
  parentdir=$(dirname "${filename}")

  if [ -d "${filename}" ]; then
      echo "$(cd "${filename}" && pwd)"
  elif [ -d "${parentdir}" ]; then
    echo "$(cd "${parentdir}" && pwd)/$(basename "${filename}")"
  fi
}
于 2014-01-17T14:24:12.573 回答
78
$ readlink -m FILE
/path/to/FILE

这比readlink -e FILEor好realpath,因为即使文件不存在它也可以工作。

于 2013-02-27T17:04:26.420 回答
75

这个相对路径到绝对路径转换器shell函数

  • 不需要实用程序(只需cdpwd
  • 适用于目录和文件
  • 把手...
  • 处理目录或文件名中的空格
  • 要求该文件或目录存在
  • 如果给定路径不存在任何内容,则不返回任何内容
  • 处理绝对路径作为输入(本质上通过它们)

代码:

function abspath() {
    # generate absolute path from relative path
    # $1     : relative filename
    # return : absolute path
    if [ -d "$1" ]; then
        # dir
        (cd "$1"; pwd)
    elif [ -f "$1" ]; then
        # file
        if [[ $1 = /* ]]; then
            echo "$1"
        elif [[ $1 == */* ]]; then
            echo "$(cd "${1%/*}"; pwd)/${1##*/}"
        else
            echo "$(pwd)/$1"
        fi
    fi
}

样本:

# assume inside /parent/cur
abspath file.txt        => /parent/cur/file.txt
abspath .               => /parent/cur
abspath ..              => /parent
abspath ../dir/file.txt => /parent/dir/file.txt
abspath ../dir/../dir   => /parent/dir          # anything cd can handle
abspath doesnotexist    =>                      # empty result if file/dir does not exist
abspath /file.txt       => /file.txt            # handle absolute path input

注意:这是基于nolan6000bsingh的答案,但修复了文件大小写。

我也明白最初的问题是关于现有的命令行实用程序。但是,由于这似乎是关于 stackoverflow 的问题,包括希望具有最小依赖关系的 shell 脚本,所以我把这个脚本解决方案放在这里,这样我以后可以找到它:)

于 2014-04-11T02:12:23.047 回答
26

find命令可能会有所帮助

find $PWD -name ex*
find $PWD -name example.log

列出当前目录中或下方名称与模式匹配的所有文件。如果您只会得到一些结果(例如,靠近树底部的目录包含少量文件),您可以简化它,只需

find $PWD

我在 Solaris 10 上使用它,它没有提到其他实用程序。

于 2011-10-12T16:26:49.443 回答
17

这是一个 zsh-only 函数,我喜欢它的紧凑性。它使用“A”扩展修饰符——参见 zshexpn(1)。

realpath() { for f in "$@"; do echo ${f}(:A); done }
于 2012-09-24T13:59:37.670 回答
16

如果您没有 readlink 或 realpath 实用程序,则可以使用以下适用于 bash 和 zsh 的功能(不确定其余功能)。

abspath () { case "$1" in /*)printf "%s\n" "$1";; *)printf "%s\n" "$PWD/$1";; esac; }

这也适用于不存在的文件(与 python 函数一样os.path.abspath)。

不幸的是abspath ./../somefile并没有摆脱这些点。

于 2010-10-12T14:59:46.237 回答
9

通常不存在 to a file 之类的东西 absolute path这种说法意味着通常可能有多个,因此使用定冠词the是不合适的)。Anabsolute path是从根“/”开始的任何路径,并指定一个独立于工作目录的无歧义文件。(参见例如wikipedia)。

Arelative path是从另一个目录开始解释的路径。如果它是relative path由应用程序操作的,它可能是工作目录(尽管不一定)。当它位于目录中的符号链接中时,它通常是相对于该目录的(尽管用户可能有其他用途)。

因此,绝对路径只是相对于根目录的路径。

路径(绝对或相对)可能包含也可能不包含符号链接。如果不是这样,它也有点不受链接结构变化的影响,但这不是必需的,甚至不是可取的。有些人调用canonical path( 或canonical file name) resolved pathabsolute path其中所有符号链接都已解决,即已被替换为链接到的路径。这两个命令realpathreadlink查找规范路径,但只有realpath一个选项可以获取绝对路径,而无需费心解析符号链接(以及其他几个选项来获取各种路径,绝对路径或相对于某个目录的路径)。

这需要几点说明:

  1. 符号链接只有在它们应该链接到的东西已经创建时才能被解析,显然情况并非总是如此。命令realpathreadlink选项可以解决这个问题。
  2. 路径上的目录以后可以成为符号链接,这意味着路径不再是canonical. 因此,这个概念是时间(或环境)相关的。
  3. 即使在理想情况下,当所有符号链接都可以解析时,一个文件仍然可能有多个符号链接,canonical path原因有两个:
    • 包含该文件的分区可能已同时 ( ro) 安装在多个安装点上。
    • 可能存在指向文件的硬链接,这意味着该文件本质上存在于几个不同的目录中。

因此,即使对 的定义更加严格canonical path,文件也可能有多个规范路径。这也意味着限定词canonical有些不足,因为它通常意味着唯一性的概念。

这扩展了对Bash上另一个类似问题的回答中对该主题的简短讨论:retrieve absolute path given relative

我的结论是,realpath它比readlink. readlink没有涵盖的唯一用途是realpath没有选项返回符号链接值的调用。

于 2013-05-19T17:40:57.507 回答
7

如果您只想使用内置函数,最简单的可能是:

find `pwd` -name fileName

只需输入额外的两个单词,这将适用于所有 unix 系统以及 OSX。

于 2019-10-24T10:54:28.080 回答
6

描述即将发生的事情的dogbane 答案:

#! /bin/sh
echo "$(cd "$(dirname "$1")"; pwd)/$(basename "$1")"

解释:

  1. 此脚本获取相对路径作为参数"$1"
  2. 然后我们得到该路径的dirname部分(您可以将 dir 或 file 传递给此脚本):dirname "$1"
  3. 然后我们进入这个相对目录并通过运行shell 命令cd "$(dirname "$1")获取它的绝对路径pwd
  4. 之后,我们将basename附加到绝对路径:$(basename "$1")
  5. 作为最后一步,echo我们
于 2017-05-20T10:03:02.533 回答
3

用自制软件回答

realpath是最好的答案,但如果你没有安装它,你必须首先运行brew install coreutils它将安装具有许多很棒的功能的coreutils 。编写一个自定义函数并导出它是太多的工作和这样的错误风险,这里有两行:

$ brew install coreutils
$ realpath your-file-name.json 
于 2020-06-01T19:02:01.067 回答
3

在某些情况下,此问题的最佳答案可能会产生误导。想象一下,您要查找其绝对路径的文件位于$PATH变量中:

# node is in $PATH variable
type -P node
# /home/user/.asdf/shims/node
cd /tmp
touch node
readlink -e node
# /tmp/node
readlink -m node
# /tmp/node
readlink -f node
# /tmp/node
echo "$(cd "$(dirname "node")"; pwd -P)/$(basename "node")"
# /tmp/node
realpath node
# /tmp/node
realpath -e node
# /tmp/node

# Now let's say that for some reason node does not exist in current directory
rm node
readlink -e node
# <nothing printed>
readlink -m node    
# /tmp/node         # Note: /tmp/node does not exist, but is printed
readlink -f node
# /tmp/node         # Note: /tmp/node does not exist, but is printed
echo "$(cd "$(dirname "node")"; pwd -P)/$(basename "node")"
# /tmp/node         # Note: /tmp/node does not exist, but is printed
realpath node
# /tmp/node         # Note: /tmp/node does not exist, but is printed
realpath -e node
# realpath: node: No such file or directory

基于以上我可以得出结论:realpath -e并且readlink -e可以用于查找我们期望存在于当前目录中的文件的绝对路径,而不会受到$PATH变量的影响。唯一的区别是realpath输出到标准错误,但如果找不到文件,两者都会返回错误代码:

cd /tmp
rm node
realpath -e node ; echo $?
# realpath: node: No such file or directory
# 1
readlink -e node ; echo $?
# 1

现在,如果您想要a 中存在的文件的绝对路径$PATH,则以下命令将是合适的,这与当前目录中是否存在同名文件无关。

type -P example.txt
# /path/to/example.txt

# Or if you want to follow links
readlink -e $(type -P example.txt)
# /originalpath/to/example.txt

# If the file you are looking for is an executable (and wrap again through `readlink -e` for following links )
which executablefile
# /opt/bin/executablefile

并且,如果缺少,则回退到$PATH,例如:

cd /tmp
touch node
echo $(readlink -e node || type -P node)
# /tmp/node
rm node
echo $(readlink -e node || type -P node)
# /home/user/.asdf/shims/node
于 2021-02-04T10:43:20.573 回答
2

对于目录dirname被绊倒../并返回./

可以修改nolan6000 的功能来解决这个问题:

get_abs_filename() {
  # $1 : relative filename
  if [ -d "${1%/*}" ]; then
    echo "$(cd ${1%/*}; pwd)/${1##*/}"
  fi
}
于 2014-03-21T07:13:49.373 回答
2

这不是问题的答案,而是针对那些编写脚本的人:

echo `cd "$1" 2>/dev/null&&pwd||(cd "$(dirname "$1")";pwd|sed "s|/*\$|/${1##*/}|")`

它正确处理 / .. ./ 等。我似乎也在 OSX 上工作

于 2016-03-16T18:57:03.107 回答
2

我在我的系统上放置了以下脚本,当我想快速获取当前目录中文件的完整路径时,我将其称为 bash 别名:

#!/bin/bash
/usr/bin/find "$PWD" -maxdepth 1 -mindepth 1 -name "$1"

我不知道为什么,但是,在 OS X 上,当被脚本“$PWD”调用时,它会扩展为绝对路径。在命令行上调用 find 命令时,它不会。但它做我想要的……享受。

于 2016-08-21T05:50:28.690 回答
1
#! /bin/bash

file="$@"
realpath "$file" 2>/dev/null || eval realpath $(echo $file | sed 's/ /\\ /g')

这弥补了它的缺点realpath,将它存储在一个shell脚本fullpath中。您现在可以调用:

$ cd && touch a\ a && rm A 2>/dev/null 
$ fullpath "a a"
/home/user/a a
$ fullpath ~/a\ a
/home/user/a a
$ fullpath A
A: No such file or directory.
于 2014-04-19T02:38:22.197 回答
1

在Ruby中获取绝对路径的另一种方法:

realpath() {ruby -e "require 'Pathname'; puts Pathname.new('$1').realpath.to_s";}

使用无参数(当前文件夹)和相对和绝对文件或文件夹路径作为参数。

于 2017-01-10T16:16:06.083 回答
1

如果您的脚本可能坚持存在 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主要用于将用户的消息打印到标准输出。echo脚本编写者所依赖的许多行为实际上是未指定的。即使是著名的也不是标准化的或for tab-n的用法。\tPOSIX 标准说:

要写入标准输出的字符串。如果第一个操作数是 -n,或者任何操作数包含字符,则结果是实现定义的
- https://pubs.opengroup.org/onlinepubs/9699919799/utilities/echo.html

因此,每当您想向标准输出写入内容并且不是为了向用户打印消息时,建议使用精确定义printf的行为。printf我的函数使用标准输出来传递结果,这不是给用户的消息,因此只使用printf保证完美的可移植性。

于 2020-09-06T11:49:14.093 回答
0

我用单行

(cd ${FILENAME%/*}; pwd)

但是,这只能在$FILENAME具有实际存在的任何类型(相对或绝对)的前导路径时使用。如果根本没有引导路径,那么答案很简单$PWD。如果前导路径不存在,则答案可能是不确定的,否则答案很简单${FILENAME%/*},如果路径是绝对的。

把这一切放在一起,我建议使用以下功能

function abspath() {
  # argument 1: file pathname (relative or absolute)
  # returns: file pathname (absolute)
  if [ "$1" == "${1##*/}" ]; then # no path at all
    echo "$PWD"
  elif [ "${1:0:1}" == "/" -a "${1/../}" == "$1" ]; then # strictly absolute path
    echo "${1%/*}"
  else # relative path (may fail if a needed folder is non-existent)
    echo "$(cd ${1%/*}; pwd)"
  fi
}

另请注意,这仅适用于bash兼容的 shell。我不相信替换在简单的 shell 中起作用sh

于 2020-12-31T15:05:23.763 回答
-1

嘿伙计们,我知道这是一个旧线程,但我只是将其发布以供其他像我一样访问过此的人参考。如果我正确理解了这个问题,我认为是locate $filename命令。它显示提供的文件的绝对路径,但前提是它存在。

于 2014-12-05T12:01:05.290 回答