问:有没有简单的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)

当前回答

试试readlink,它将解析符号链接:

readlink -e /foo/bar/baz

其他回答

这并不是问题的答案,但对于那些编写脚本的人来说:

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

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

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

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

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

Alexander Klimetschek的答案是可以的,如果您的脚本可能坚持使用bash或bash兼容的shell。它不能与只符合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主要用于将消息打印给用户到标准输出。脚本编写者所依赖的许多回显行为实际上是未指定的。甚至著名的-n也没有标准化,tab的\t用法也没有标准化。POSIX标准说:

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

因此,每当您想要向stdout写入一些内容,而不是为了将消息打印给用户时,建议使用printf,因为printf的行为是精确定义的。我的函数使用stdout传递一个结果,这不是给用户的消息,因此只使用printf保证了完美的可移植性。

我用单线

(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中起作用。

试试readlink,它将解析符号链接:

readlink -e /foo/bar/baz