bash / fish命令打印文件的绝对路径


448

问题:是否有一个简单的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)

我认为@DennisWilliamson的答案(使用-m选项)在(通常)更具可移植性和处理不存在的文件方面具有优越性。
TTT 2014年

还是弗利姆的答案;两者都是很好的解决方案。然而,班尼尔(Bannier's)最好地回答了原来的问题。
dhardy 2014年

3
OSX用户:看到这个答案
伊恩·

Answers:


560

尝试realpath

$ realpath example.txt
/home/username/example.txt

133
+1,非常棒的程序。但是请注意,这是一个外部命令(不是内置的Shell),默认情况下可能不会在每个系统中都存在,即,您可能需要安装它。在Debian中,它以相同的名称驻留在单独的软件包中。
Roman Cheplyaka

5
我在路径中添加了以下realpath内容:github.com/westonruter/misc-cli-tools/blob/master/realpath
Weston Ruter

1
@Flimm:给定一些相对路径,它仍然会为您提供有效路径。如果您想了解文件的存在,请使用其他工具,例如测试。
本杰明·班尼尔

14
@Dalin:尝试安装coreutils。二进制文件名为grealpath。
Toni Penya-Alba'2

5
该工具是在GNU coreutils 8.15(2012年)中引入的,因此非常新。
2014年

317

尝试readlink解析符号链接:

readlink -e /foo/bar/baz

50
我宁愿使用“ -f”而不是“ -e”,以便我们可以看到不存在文件的绝对路径。
hluk 2010年

5
对我来说,这似乎是最简单的方法,同时也便于携带。readlink是gnu coreutils的端口,因此它会安装在几乎所有Linux发行版中,除了一些嵌入式发行版。
2010年

14
在我看来,这-m是最好的选择。
马特·乔纳

1
@达林:/usr/bin/readlink在小牛上。或者您是说OS X找不到这些选项?它似乎是一个受阻的BSD版本...:p
iconoclast 2014年

14
@iconoclast这些选项在OSX上不可用,并且根据BSD readlink联机帮助页:only the target of the symbolic link is printed. If the given argument is not a symbolic link, readlink will print nothing and exit with an error,因此readlink在OSX上无法实现此目的
SnoringFrog

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

8
不要忘记引用所有内容。如果您不明白为什么,请尝试在文件上运行程序a b(a和b之间有两个空格,SO会吃掉其中一个)。
Roman Cheplyaka

1
也可以尝试在“ /”上转换为“ ///”
乔治(George)

34
到目前为止,由于readlink和realpath都不是POSIX,所以这是唯一不需要您编写和编译可执行文件的POSIX解决方案
Ciro Santilli郝海东冠状病六四事件法轮功

25
我曾经在OS X上将function realpath { echo $(cd $(dirname $1); pwd)/$(basename $1); }自己作为realpath命令(当前接受的答案)。谢谢!
James Bedford 2014年

1
我使用此方法来定义脚本的日志文件名:LOG=$(echo $(cd $(dirname $0); pwd)/$(basename $0 .sh).log)
DrStrangepork 2014年

83

忘掉readlinkrealpath可能会或可能不会被你的系统上安装。

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

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

然后可以像这样使用它:

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

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

该解决方案利用了以下事实:pwd当不带参数调用时,Bash内置命令将显示当前目录的绝对路径。

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

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

如果dir不存在怎么办?

如上所述,如果给定的目录路径不存在,该功能将失败并在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
}

功能不错,但显然您不明白这个问题。我想要一些可交互使用的东西,而不是脚本。
dhardy 2014年

3
我想补充一点,realpath来自coreutils软件包,其中也包含工具,如whotouchcat。它是一套非常标准的GNU工具,因此您可以确定它几乎可以安装在任何基于linux的机器上,也就是说,您是对的:它有2个问题:i)您可能会在默认安装时错过它非GNU Unix系统(如OpenBSD)ii)此工具是在8.15版中引入的(因此从2012年开始是相当新的),因此在长期稳定的系统(如RHEL 6)上您会错过它。
2014年

1
好吧,至少Continuum Analytics(Anaconda Python发行版的制造商)似乎很喜欢这个答案。它是在其“激活”脚本中实现的(参考链接返回此处),该脚本用于激活conda工具创建的虚拟环境。所以……好人!
wjv 2014年

1
这仅适用于目录(因为没有其他目录),无法处理“ ..”和“。”。看到我的回答应该可以解决所有问题:stackoverflow.com/a/23002317/2709
Alexander Klimetschek 2014年

3
@marbu realpath在Ubuntu 14.04或Debian Wheezy上不存在。随着时间的流逝,它可能会变得有些标准,但现在肯定不是。另请注意,OP没有对Linux或GNU进行任何说明。Bash的使用更为广泛。
mc0e 2014年

76
$ readlink -m FILE
/path/to/FILE

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


真好 这也适用于确实存在但不是符号链接的文件/目录。
quietmint 2013年

我确实在OS X 10.9(Mavericks)上有readlink,所以这绝对是这里的最佳答案。
肯尼斯·哈斯特2014年

6
@KennethHoste:的 -m选项在Mavericks上不可用。
iconoclast 2014年

2
绝对不是在DEFAULT OSX安装中。
Fran Marzoa

在Linux(CentOS)上可以工作,谢谢,因为它不存在realpath
jimh,2016年

65

相对路径转换器外壳函数的相对路径

  • 不需要实用程序(只需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脚本,因此我将此脚本解决方案放在这里,以便稍后找到它:)


谢谢。这就是我去与OSX
格雷格-

这不适用于带有空格的目录,即:$ abspath 'a b c/file.txt'这是因为cd未引用to的参数。为了克服这个问题,而不是引用整个参数echo(是否有这些引号?)仅引用路径参数。如更换echo "$(cd ${1%/*}; pwd)/${1##*/}"echo $(cd "${1%/*}"; pwd)/${1##*/}
乔治,

@george感谢您注意,我已修复它。
Alexander Klimetschek 2014年

1
我浏览了几十个半精打采的解决方案,这肯定是最简洁,最准确的纯bash解决方案。您可以替换echo "$(cd "$1"; pwd)"为来进一步缩小它(cd "$1"; pwd)。这里不需要回声。

@Six True,已更新。大括号( ... )仍然是必需的,所以这cd发生在子shell中,因为我们不想为任何调用者更改工作目录abspath
Alexander Klimetschek 2015年

24

find命令可能会有所帮助

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

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

find $PWD

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


1
一读时我没有看过这个评论;这里的关键是,如果给find命令提供绝对路径,则它将以绝对路径输出结果。因此使用$ PWD是您所在位置的绝对路径,因此您将获得绝对输出。
simbo1905 2013年

4
如果您依靠PWD,则可以简单地使用$PWD/$filename
jonny 2014年

使用-maxdepth 1可以改进此方法。另外,请不要忘记引用-这里的潜在安全问题,具体取决于您如何使用它。
mc0e 2014年

1
@jonny:如果$ filename已经是绝对的,则$ PWD / $ filename失败。
mc0e 2014年

如果文件名指定为“ ./filename.txt”(带前导点和斜杠),则此操作将失败。
Mark Stosberg

15

如果您没有readlink或realpath实用程序,则可以使用在bash和zsh中起作用的以下函数(不确定其余部分)。

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

这也适用于不存在的文件(python函数也是如此) os.path.abspath)。

不幸的是,abspath ./../somefile它并没有摆脱一切。


1
对我来说看起来很便携。另一方面,将在包含换行符的文件名上中断。
Roman Cheplyaka

1
要进一步改进,请替换echo "$1"printf "%s\n" "$1"(与第二个回显相同)。echo可能会在其参数中解释反斜杠。
Roman Cheplyaka

再次,你是对的!实际上,zsh中echo命令的行为与bash不同。
hluk 2010年

1
严格来说,在POSIX下,如果参数包含反斜杠,则echo的行为是不确定的。但是,它是在XSI扩展名下定义的(即echo应该解释转义序列)。但是bash和zsh都离POSIX合规性
还差

抱歉,我看不到重点。我已经作为脚本提供了答案,它具有更多的功能,并且不需要在Shell脚本中使用。
dhardy 2010年

10

我喜欢它的紧凑性,这是仅zsh函数。它使用'A'扩展修饰符-请参见zshexpn(1)。

realpath() { for f in "$@"; do echo ${f}(:A); done }

哇,这是一个优雅的解决方案!
ShellFish 2014年

6

通常不会有这样的事情 absolute path一个文件(这个声明意味着有可能不止一个,一般,因此使用了定冠词是不恰当的)。An absolute path是从根目录“ /”开始并且指定文件的唯一路径,与工作目录无关(例如,参见Wikipedia)。

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

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

路径(绝对路径或相对路径)可能包含也可能不包含符号链接。如果不是这样,则在某种程度上也不能改变链接结构,但这不是必须的,甚至是不希望的。有人叫canonical path(或canonical file nameresolved path)的absolute path其中所有符号链接都得到了解决,即已被替换到whetever它们链接到的路径。这些命令realpath和命令readlink都寻找规范路径,但是只有realpath一个获取绝对路径的选项,而不必费心去解析符号链接(连同其他几个选项来获取各种路径,无论是绝对路径还是相对于某个目录)。

这需要一些说明:

  1. 仅当已经创建了符号链接时,才可以解析符号链接,这显然并非总是如此。这些命令realpathreadlink具有解决方案的选项。
  2. 路径上的目录以后可以变成符号链接,这意味着该路径不再canonical。因此,该概念取决于时间(或环境)。
  3. 即使在理想情况下,当所有符号链接都可以解析时,由于canonical path两个原因,一个文件可能仍然不止一个:
    • 包含文件的分区可能已同时(ro)挂载在几个挂载点上。
    • 可能存在指向该文件的硬链接,这实际上意味着该文件存在于几个不同的目录中。

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

这扩展了对该主题的简短讨论,并回答了Bash另一个类似的问题:检索给定相对路径的绝对路径

我的结论是,realpath与相比,它设计得更好并且更具灵活性readlink。唯一readlink没有被使用的用途是不realpath带选项返回符号链接值的调用。


我不介意被否决,但是我想理解为什么这样做,以便我可以学习一些技术上的知识或关于在站点上被视为正确做法的知识。好吧……至少这促使我向Meta Stack Overflow注册,以更好地了解本地社会学。我推荐它。-不过,如果有人对我的回答是否适当有意见,我很感兴趣。
2013年

1
(a)这个问题有2 + 1/2年前的有效答案,(b)有一个(单个)绝对路径对应一个相对路径(这就是我想要的),尽管可能不是您所需要的文件指出。顺便说一句,关于realpathvs readlink甚至符号链接的任何评论都超出了我的要求。
dhardy

1
第二点评论:简短的答案通常比冗长的讲授有用。堆栈溢出通常用于询问特定问题。
dhardy

5
1-问题有有效答案的时间过长,这并不意味着该问题已经结束。这些问题并非仅供个人使用。确实,有一些徽章可以收集比经过验证的答案更多的选票。借助新工具,“最佳”答案会随着时间而改变。许多人使用通过网络搜索访问的旧答案。
2013年

1
2-您坚持存在与相对路径相对应的(唯一)绝对路径。虽然我确实猜出您所说的“对应”是什么意思,即通过一组给定的转换从原始路径派生的路径,但是您的陈述仍然不正确。有一个独特的“对应”规范路径。这个词canonical相对于一组转换恰好是指这种唯一性。但是,例如,尽管/ dev / cdrom实际上是/ dev / sr0的链接,但却是一个完美的绝对路径。两者都指向同一设备。我的原始答案指向相关的网络文章。
13年

5

描述的dogbane 答案是怎么回事:

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

说明:

  1. 该脚本获取相对路径作为参数 "$1"
  2. 然后,我们获得该路径的目录名部分(您可以将目录或文件传递给此脚本):dirname "$1"
  3. 然后我们cd "$(dirname "$1")进入这个相对目录并通过运行pwdshell命令获取它的绝对路径
  4. 之后,我们将基本名称附加到绝对路径:$(basename "$1")
  5. 作为最后一步,我们echo

2

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

可以修改nolan6000的功能以修复以下问题:

get_abs_filename() {
  # $1 : relative filename
  if [ -d "${1%/*}" ]; then
    echo "$(cd ${1%/*}; pwd)/${1##*/}"
  fi
}

1
欢迎来到SO!对于较小的更改和备注,向现有答案添加注释可能比添加答案更合适,尤其是如果此复制的答案缺少原始答案的许多其他信息时,尤其如此。获得更多声誉后,便可以在其他人的答案中添加评论。目前,尝试通过提出独特的,有价值的问题或提供独立的良好答案来收集声誉。
cfi

1
由于您指的是nolan6000的答案,请注意,发帖人dhardy已经评论说,他将不接受nolan6000的答案,因为他不在寻找脚本。因此严格来说,您的答案不能回答问题。
cfi

该功能可以添加到.profile交互使用中。
adib

如果$1使用纯文件名,则将无法使用:get_abs_filename foo->无(或<current_dir>/foo/foo如果foo使用目录)。
ivan_pozdeev

2

这不是问题的答案,但是对于那些执行脚本的人:

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

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


2

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

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

我不确定为什么,但是,在OS X上,当由脚本“ $ PWD”调用时,它会扩展到绝对路径。在命令行上调用find命令时,不会。但这确实满足了我的需求。


$PWD始终具有工作目录的绝对路径。另外,find斜线不能容忍,因此您不会按照要求回答问题。更快的方法做你希望做的,简直是什么echo "$PWD/$1"
亚当·卡茨

2

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

find `pwd` -name fileName

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


我会在-maxdepth 1之前添加-name(假设文件位于当前目录中)。没有它,它将与pwd的任何子目录中的文件一起使用,而与其他文件一起使用。
Walter Tross

确实您是对的,该问题确实指定了该文件将在当前目录中。你是什​​么意思,“但不能和别人在一起”?
Arj

我的意思是find不会在目录树下找到文件pwd。例如,如果尝试这样做find . -name ../existingFile,它将失败。
Walter Tross

足够公平,是的,这假设您要在cwd中打印文件的完整路径(按照原始问题),或者在cwd树中的任何位置(不在原始问题中)打印文件的完整路径。
Arj

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.

它能解决什么?realpath "foo bar"-> /home/myname/foo bar。如果您不介意引用命令行参数,那不是realpath错。
ivan_pozdeev '18年

同意,它更多是一个方便包装。
ShellFish

1

Ruby中获取绝对路径的一种替代方法:

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

没有参数(当前文件夹),并且相对和绝对文件或文件夹路径作为参数工作。


0

用自制软件回答

realpath是最好的答案,但是如果您没有安装它,则必须首先运行brew install coreutils,这将安装具有许多很棒功能的coreutils。编写自定义函数并将其导出会产生大量工作,并且会出现诸如此类的错误,这是两行内容:

$ brew install coreutils
$ realpath your-file-name.json 

-1

大家好,我知道这是一个老话题,但我只是将其发布给其他像我这样访问过的人。如果我正确理解了这个问题,我认为是locate $filename命令。它显示提供的文件的绝对路径,但仅在存在的情况下。


4
locate是用于按名称搜索文件/路径的实用程序。它可以完成此任务,但也可能找到其他匹配项,并且可能需要先updatedb以root用户身份调用才能找到现有文件。
dhardy 2014年
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.