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

时间:2010-10-12 13:12:10

标签: bash shell path

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

用法案例:我在目录/a/b中,我想在命令行上打印文件c的完整路径,以便我可以轻松地将其粘贴到另一个程序中:{ {1}}。简单但是一个小程序可以在处理长路径时节省大约5秒钟,这最终会增加。所以让我感到惊讶的是,我找不到一个标准的实用工具来做到这一点 - 真的没有吗?

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

/a/b/c

22 个答案:

答案 0 :(得分:429)

尝试realpath

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

答案 1 :(得分:294)

尝试解析符号链接的readlink

readlink -e /foo/bar/baz

答案 2 :(得分:223)

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

答案 3 :(得分:73)

$ readlink -m FILE
/path/to/FILE

这比readlink -e FILErealpath更好,因为即使文件不存在也能正常工作。

答案 4 :(得分:73)

忘记您系统上可能安装或未安装的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命令将打印当前目录的绝对路径。

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

它是可移植的,不需要readlinkrealpath,这在给定的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
}

答案 5 :(得分:58)

此绝对路径转换器的相对路径 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

注意:这取决于答案from nolan6000bsingh,但修复了文件大小写。

我也理解原始问题是关于现有的命令行实用程序。但是因为这似乎是stackoverflow的问题,包括想要具有最小依赖性的shell脚本,我把这个脚本解决方案放在这里,所以我可以在以后找到它:)

答案 6 :(得分:23)

find命令可能有帮助

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

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

find $PWD

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

答案 7 :(得分:14)

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

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

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

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

答案 8 :(得分:9)

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

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

答案 9 :(得分:7)

文件中通常没有 absolute path这样的东西(这句话意味着一般可能有多个,因此使用定冠词 不合适)。 absolute path是从根“/”开始的任何路径,并指定一个没有歧义的文件,与工作目录无关。(参见例如wikipedia)。

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

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

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

这需要几个评论:

  1. 符号链接只有在符合条件的情况下才能解析 链接到已经创建,显然并非总是如此。命令realpathreadlink可以选择对此进行说明。
  2. 路径上的目录以后可以成为符号链接,这意味着该路径不再是canonical。因此,概念是时间(或环境)依赖。
  3. 即使在理想的情况下,当所有符号链接都可以解决时, 两个文件可能仍有多个canonical path 原因:
    • 包含该文件的分区可能已同时挂载(ro)在几个挂载点上。
    • 可能存在指向该文件的硬链接,这意味着该文件基本上存在于几个不同的目录中。
  4. 因此,即使对canonical path的限制性更强,也可能存在一些文件的规范路径。这也意味着限定符canonical有些不足,因为它通常意味着唯一性的概念。

    这在Bash: retrieve absolute path given relative

    的另一个类似问题的答案中扩展了对该主题的简短讨论

    我的结论是realpath设计得更好,比readlink更灵活。 readlink未涵盖realpath的唯一用途是没有选项的调用返回符号链接的值。

答案 10 :(得分:3)

dogbane answer及其描述内容:

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

说明:

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

答案 11 :(得分:2)

对于目标dirname../会被触发并返回./

可以修改

nolan6000's function来修复:

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

答案 12 :(得分:2)

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

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

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

答案 13 :(得分:2)

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

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

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

答案 14 :(得分: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.

答案 15 :(得分:1)

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

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

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

答案 16 :(得分:0)

用自制软件回答

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

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

答案 17 :(得分:0)

如果您的脚本可能坚持存在bash或bash兼容shell,那么Alexander Klimetschek的答案是可以的。不适用于仅符合POSIX的外壳。

另外,当最终文件是根目录中的文件时,输出将为//file,从技术上讲这不是错误的(系统将双/视为单个#!/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" ),但看起来很奇怪。

这是一个适用于每个符合POSIX的shell的版本,POSIX标准也需要它使用的所有外部工具,并且它显式处理根文件的情况:

result=$(cd "$dir" && pwd)

将其放入文件中并使其可执行,您有了CLI工具,可以快速获取文件和目录的绝对路径。或者只是复制该函数并在您自己的符合POSIX的脚本中使用它。它将相对路径转换为绝对路径,并按原样返回绝对路径。

有趣的修改

如果将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主要用于打印消息以供用户输出。实际上,脚本作者所依赖的许多-n行为是未指定的。甚至没有著名的\t或选项卡使用printf的标准化。 POSIX标准说:

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

因此,每当您要写一些东西到stdout而不是为了向用户打印消息时,建议使用printf,因为printf的行为已被准确定义。我的函数使用stdout传递结果,这不是给用户的消息,因此仅使用pg_total_relation_size('table_name')可以保证完美的可移植性。

答案 18 :(得分: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 中起作用。

答案 19 :(得分:0)

在某些情况下,此问题的最佳答案可能会产生误导。假设您要查找其绝对路径的文件位于 $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 -ereadlink -e 可用于查找我们希望存在于当前目录中的文件的绝对路径 , 结果不受$PATH变量的影响。唯一的区别是 realpath 输出到 stderr,但如果找不到文件,两者都会返回错误代码:

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

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

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

答案 20 :(得分:-1)

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

答案 21 :(得分:-1)

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

find `pwd` -name fileName

仅需键入两个额外的单词,这将在所有unix系统以及OSX上正常工作。