如何在Bash中规范化文件路径?

时间:2023-01-05 04:18:00

I want to transform /foo/bar/.. to /foo

我想转换/foo/bar/。/ foo

Is there a bash command which does this?

是否有一个bash命令来执行这个操作?


Edit: in my practical case, the directory does exist.

编辑:在我的实际案例中,目录确实存在。

20 个解决方案

#1


145  

if you're wanting to chomp part of a filename from the path, "dirname" and "basename" are your friends, and "realpath" is handy too.

如果您想要从路径中删除文件名,“dirname”和“basename”是您的朋友,“realpath”也很方便。

dirname /foo/bar/baz 
# /foo/bar 
basename /foo/bar/baz
# baz
dirname $( dirname  /foo/bar/baz  )) 
# /foo 
realpath ../foo
# ../foo: No such file or directory
realpath /tmp/../tmp/../tmp
# /tmp

Edit

编辑

Realpath appears not to be standard issue.

Realpath似乎不是标准问题。

The closest you can get with the stock standard is

你最接近股票标准的是。

readlink -f  /path/here/.. 

Realpath appears to come from debian, and is not part of coreutils: http://packages.debian.org/unstable/utils/realpath Which was originally part of the DWWW package.

Realpath似乎来自debian,它不是coreutils的一部分:http://packages.debian.org/unstable/utils/realpath,它最初是DWWW包的一部分。

( also available on gentoo as app-admin/realpath )

(也可用于gentoo as app-admin/realpath)

readlink -m /path/there/../../ 

Works the same as

一样的工作

 realpath -s /path/here/../../

in that it doesn't need the path to actually exist to normalise it.

在这一点上,它不需要路径来实现它的正常化。

#2


80  

I don't know if there is a direct bash command to do this, but I usually do

我不知道是否有直接的bash命令来执行这个操作,但我通常会这样做。

normalDir="`cd "${dirToNormalize}";pwd`"
echo "${normalDir}"

and it works well.

它工作得很好。

#3


51  

Try realpath. Below is the source in its entirety, hereby donated to the public domain.

realpath试试。下面是它的全部来源,特此捐赠给公共领域。

// realpath.c: display the absolute path to a file or directory.
// Adam Liss, August, 2007
// This program is provided "as-is" to the public domain, without express or
// implied warranty, for any non-profit use, provided this notice is maintained.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libgen.h>   
#include <limits.h>

static char *s_pMyName;
void usage(void);

int main(int argc, char *argv[])
{
    char
        sPath[PATH_MAX];


    s_pMyName = strdup(basename(argv[0]));

    if (argc < 2)
        usage();

    printf("%s\n", realpath(argv[1], sPath));
    return 0;
}    

void usage(void)
{
    fprintf(stderr, "usage: %s PATH\n", s_pMyName);
    exit(1);
}

#4


35  

Use the readlink utility from the coreutils package.

使用coreutils包中的readlink实用程序。

MY_PATH=$(readlink -f "$0")

#5


30  

A portable and reliable solution is to use python, which is preinstalled pretty much everywhere (including Darwin). You have two options:

一个可移植和可靠的解决方案是使用python,它在任何地方都是预先安装的(包括Darwin)。你有两个选择:

  1. abspath returns an absolute path but does not resolve symlinks:

    abspath返回一个绝对路径,但不解析符号链接:

    python -c "import os,sys; print os.path.abspath(sys.argv[1])" path/to/file

    python - c”导入系统,系统;打印os.path.abspath(sys.argv[1])”路径/ /文件

  2. realpath returns an absolute path and in doing so resolves symlinks, generating a canonical path:

    realpath返回一个绝对路径,并在此过程中解析符号链接,生成规范路径:

    python -c "import os,sys; print os.path.realpath(sys.argv[1])" path/to/file

    python - c”导入系统,系统;打印os.path.realpath(sys.argv[1])”路径/ /文件

In each case, path/to/file can be either a relative or absolute path.

在每种情况下,path/to/文件都可以是相对的,也可以是绝对路径。

#6


13  

readlink is the bash standard for obtaining the absolute path. It also has the advantage of returning empty strings if paths or a path doesn't exist (given the flags to do so).

readlink是获取绝对路径的bash标准。它还具有返回空字符串的优势,如果路径或路径不存在(给定标记这样做)。

To get the absolute path to a directory that may or may not exist, but who's parents do exist, use:

为了获得一个目录的绝对路径,该目录可能存在,也可能不存在,但是谁的父母确实存在,使用:

abspath=$(readlink -f $path)

To get the absolute path to a directory that must exist along with all parents:

要获得与所有父母同时存在的目录的绝对路径:

abspath=$(readlink -e $path)

To canonicalise the given path and follow symlinks if they happen to exist, but otherwise ignore missing directories and just return the path anyway, it's:

要将给定的路径规范化,并在它们碰巧存在时遵循符号链接,但是忽略丢失的目录并返回路径,它是:

abspath=$(readlink -m $path)

The only downside is that readlink will follow links. If you do not want to follow links, you can use this alternative convention:

唯一的缺点是readlink会跟随链接。如果您不想遵循链接,您可以使用这个替代约定:

abspath=$(cd ${path%/*} && echo $PWD/${path##*/})

That will chdir to the directory part of $path and print the current directory along with the file part of $path. If it fails to chdir, you get an empty string and an error on stderr.

将chdir指向$path的目录部分,并打印当前目录以及$path的文件部分。如果它失败到chdir,就会得到一个空字符串和stderr上的错误。

#7


7  

My recent solution was:

我最近的解决方案是:

pushd foo/bar/..
dir=`pwd`
popd

Based on the answer of Tim Whitcomb.

基于Tim Whitcomb的回答。

#8


5  

As Adam Liss noted realpath is not bundled with every distribution. Which is a shame, because it is the best solution. The provided source code is great, and I will probably start using it now. Here is what I have been using until now, which I share here just for completeness:

正如Adam Liss所指出的,realpath并不是与每个发行版捆绑在一起的。这是一种耻辱,因为它是最好的解决方案。提供的源代码很好,我现在可能会开始使用它。下面是我一直在使用的,我在这里分享的只是为了完整性:

get_abs_path() {
     local PARENT_DIR=$(dirname "$1")
     cd "$PARENT_DIR"
     local ABS_PATH="$(pwd)"/"$(basename "$1")"
     cd - >/dev/null
     echo "$ABS_PATH"
} 

If you want it to resolve symlinks, just replace pwd with pwd -P.

如果您希望它解决符号链接,只需用pwd -P替换pwd。

#9


4  

Talkative, and a bit late answer. I need to write one since I'm stuck on older RHEL4/5. I handles absolute and relative links, and simplifies //, /./ and somedir/../ entries.

多话,有点晚的回答。我需要写一个,因为我被困在老的RHEL4/5。我处理绝对和相对的链接,并简化//。/和somedir / . ./条目。

test -x /usr/bin/readlink || readlink () {
        echo $(/bin/ls -l $1 | /bin/cut -d'>' -f 2)
    }


test -x /usr/bin/realpath || realpath () {
    local PATH=/bin:/usr/bin
    local inputpath=$1
    local changemade=1
    while [ $changemade -ne 0 ]
    do
        changemade=0
        local realpath=""
        local token=
        for token in ${inputpath//\// }
        do 
            case $token in
            ""|".") # noop
                ;;
            "..") # up one directory
                changemade=1
                realpath=$(dirname $realpath)
                ;;
            *)
                if [ -h $realpath/$token ] 
                then
                    changemade=1
                    target=`readlink $realpath/$token`
                    if [ "${target:0:1}" = '/' ]
                    then
                        realpath=$target
                    else
                        realpath="$realpath/$target"
                    fi
                else
                    realpath="$realpath/$token"
                fi
                ;;
            esac
        done
        inputpath=$realpath
    done
    echo $realpath
}

mkdir -p /tmp/bar
(cd /tmp ; ln -s /tmp/bar foo; ln -s ../.././usr /tmp/bar/link2usr)
echo `realpath /tmp/foo`

#10


4  

Not exactly an answer but perhaps a follow-up question (original question was not explicit):

这不是一个确切的答案,但可能是一个后续问题(最初的问题不明确):

readlink is fine if you actually want to follow symlinks. But there is also a use case for merely normalizing ./ and ../ and // sequences, which can be done purely syntactically, without canonicalizing symlinks. readlink is no good for this, and neither is realpath.

如果您想要遵循符号链接,那么readlink就很好。但也有一个用例仅仅是为了规范。/和//序列,它可以完全从语法上进行,没有规范化的符号链接。readlink对这一点没有好处,realpath也不是。

for f in $paths; do (cd $f; pwd); done

works for existing paths, but breaks for others.

对现有路径有效,但对其他路径无效。

A sed script would seem to be a good bet, except that you cannot iteratively replace sequences (/foo/bar/baz/../.. -> /foo/bar/.. -> /foo) without using something like Perl, which is not safe to assume on all systems, or using some ugly loop to compare the output of sed to its input.

一个sed脚本似乎是一个不错的选择,除非您不能迭代地替换序列(/foo/bar/baz/.. .)- > / foo / bar / . .-> /foo)不使用类似Perl的东西,这在所有系统上都是不安全的,或者使用一些丑陋的循环来比较sed的输出结果。

FWIW, a one-liner using Java (JDK 6+):

FWIW,一种使用Java (JDK 6+)的单行代码:

jrunscript -e 'for (var i = 0; i < arguments.length; i++) {println(new java.io.File(new java.io.File(arguments[i]).toURI().normalize()))}' $paths

#11


4  

I'm late to the party, but this is the solution I've crafted after reading a bunch of threads like this:

我在派对上迟到了,但这是我在阅读了一大堆这样的帖子后的解决方案:

resolve_dir() {
        (builtin cd `dirname "${1/#~/$HOME}"`'/'`basename "${1/#~/$HOME}"` 2>/dev/null; if [ $? -eq 0 ]; then pwd; fi)
}

This will resolve the absolute path of $1, play nice with ~, keep symlinks in the path where they are, and it won't mess with your directory stack. It returns the full path or nothing if it doesn't exist. It expects $1 to be a directory and will probably fail if it's not, but that's an easy check to do yourself.

这将解决$1的绝对路径,与~保持友好关系,在它们所在的路径上保持符号链接,并且不会打乱您的目录堆栈。如果它不存在,它将返回完整的路径。它期望$1是一个目录,如果不是,它很可能会失败,但是这是一个简单的检查。

#12


4  

Old question, but there is much simpler way if you are dealing with full path names at the shell level:

老问题,但是如果在shell级别上处理完整路径名,会有更简单的方法:

   abspath="$( cd "$path" && pwd )"

As the cd happens in a subshell it does not impact the main script.

当cd在子shell中发生时,它不会影响主脚本。

Two variations, supposing your shell built-in commands accept -L and -P, are:

假设您的shell内置命令接受-L和-P,两个变量是:

   abspath="$( cd -P "$path" && pwd -P )"    #physical path with resolved symlinks
   abspath="$( cd -L "$path" && pwd -L )"    #logical path preserving symlinks

Personally, I rarely need this later approach unless I'm fascinated with symbolic links for some reason.

就我个人而言,我很少需要这种后来的方法,除非我因为某种原因着迷于符号链接。

FYI: variation on obtaining the starting directory of a script which works even if the script changes it's current directory later on.

FYI:获取一个脚本的起始目录的变体,即使脚本在稍后更改它的当前目录。

name0="$(basename "$0")";                  #base name of script
dir0="$( cd "$( dirname "$0" )" && pwd )"; #absolute starting dir

The use of CD assures you always have the absolute directory, even if the script is run by commands such as ./script.sh which, without the cd/pwd, often gives just .. Useless if the script does a cd later on.

使用CD确保您始终拥有绝对目录,即使脚本是由诸如./script之类的命令运行的。没有cd/pwd,通常只提供。如果脚本稍后执行一个cd,那就没有用了。

#13


3  

Try our new Bash library product realpath-lib that we have placed on GitHub for free and unencumbered use. It's thoroughly documented and makes a great learning tool.

尝试我们的新Bash库产品realpathlib,我们已经将其放置在GitHub上,以获得免费和不受限制的使用。它被详尽地记录下来并成为一个很棒的学习工具。

It resolves local, relative and absolute paths and doesn't have any dependencies except Bash 4+; so it should work just about anywhere. It's free, clean, simple and instructive.

它解决了本地、相对和绝对路径,除了Bash 4+之外没有任何依赖关系;所以它应该在任何地方工作。它是免费的,干净的,简单的和有教育意义的。

You can do:

你能做什么:

get_realpath <absolute|relative|symlink|local file path>

This function is the core of the library:

该函数是库的核心:

function get_realpath() {

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

}

It also contains functions to get_dirname, get_filename, get_ stemname and validate_path. Try it across platforms, and help to improve it.

它还包含了get_dirname、get_filename、get_ stemname和validate_path的函数。尝试跨平台,并帮助改进它。

#14


1  

The problem with realpath is that it is not available on BSD (or OSX for that matter). Here is a simple recipe extracted from a rather old (2009) article from Linux Journal, that is quite portable:

realpath的问题是它不能在BSD(或OSX)上使用。这里有一个简单的食谱,从一个相当古老的(2009年)的Linux日志中摘录,这是非常便携的:

function normpath() {
  # Remove all /./ sequences.
  local path=${1//\/.\//\/}

  # Remove dir/.. sequences.
  while [[ $path =~ ([^/][^/]*/\.\./) ]]; do
    path=${path/${BASH_REMATCH[0]}/}
  done
  echo $path
}

Notice this variant also does not require the path to exist.

注意,这个变体也不需要路径存在。

#15


1  

Based on @Andre's answer, I might have a slightly better version, in case someone is after a loop-free, completely string-manipulation based solution. It is also useful for those who don't want to dereference any symlinks, which is the downside of using realpath or readlink -f.

基于@Andre的回答,我可能会有一个稍微好一点的版本,以防有人在一个无环的、完全基于字符串的解决方案之后。它对于那些不想取消任何符号链接的人也很有用,这是使用realpath或readlink -f的缺点。

It works on bash versions 3.2.25 and higher.

它适用于bash版本3.2.25和更高版本。

shopt -s extglob

normalise_path() {
    local path="$1"
    # get rid of /../ example: /one/../two to /two
    path="${path//\/*([!\/])\/\.\./}"
    # get rid of /./ and //* example: /one/.///two to /one/two
    path="${path//@(\/\.\/|\/+(\/))//}"
    # remove the last '/.'
    echo "${path%%/.}"
}

$ normalise_path /home/codemedic/../codemedic////.config
/home/codemedic/.config

#16


0  

Based on loveborg's excellent python snippet, I wrote this:

基于loveborg优秀的python代码片段,我写道:

#!/bin/sh

# Version of readlink that follows links to the end; good for Mac OS X

for file in "$@"; do
  while [ -h "$file" ]; do
    l=`readlink $file`
    case "$l" in
      /*) file="$l";;
      *) file=`dirname "$file"`/"$l"
    esac
  done
  #echo $file
  python -c "import os,sys; print os.path.abspath(sys.argv[1])" "$file"
done

#17


0  

FILEPATH="file.txt"
echo $(realpath $(dirname $FILEPATH))/$(basename $FILEPATH)

This works even if the file doesn't exist. It does require the directory containing the file to exist.

即使文件不存在,也可以这样做。它确实需要包含文件的目录存在。

#18


0  

I know this is an ancient question. I'm still offering an alternative. Recently I met the same issue and found no existing and portable command to do that. So I wrote the following shell script which includes a function that can do the trick.

我知道这是一个古老的问题。我还在提供另一种选择。最近我遇到了同样的问题,发现没有现成的和可移植的命令可以做到这一点。因此,我编写了下面的shell脚本,其中包含一个可以执行这个技巧的函数。

#! /bin/sh

function normalize {
  local rc=0
  local esed
  local ret

  if [ "x`uname -o | grep -i 'darwin'`" != "x" ] ; then
    esed="sed -E"
  else
    esed="sed -r"
  fi


  if [ $# -gt 0 ] ; then
    # invalid
    if [ "x`echo $1 | grep -E -e '^/\.\.'`" != "x" ] ; then
      echo $1
      return -1
    fi

    # convert to absolute path
    if [ "x`echo $1 | grep -E -e '^\/'`" == "x" ] ; then
      normalize "`pwd`/$1"
      return $?
    fi

    ret=`echo $1 | $esed 's;/\.($|/);/;g' | $esed 's;/[^/]*[^/.]+[^/]*/\.\.($|/);/;g'`
  else
    read line
    normalize "$line"
    return $?
  fi

  if [ "x`echo $ret | grep -E -e '/\.(/|$)'`" != "x" ] ; then
    ret=`normalize "$ret"`
    rc=$?
  fi


  if [ "x`echo $ret | grep -E -e '/\.\.(/|$)'`" != "x" ] ; then
    ret=`normalize "$ret"`
    rc=$?
  fi

  echo "$ret"
  return $rc
}

https://gist.github.com/bestofsong/8830bdf3e5eb9461d27313c3c282868c

https://gist.github.com/bestofsong/8830bdf3e5eb9461d27313c3c282868c

#19


-1  

I discovered today that you can use the stat command to resolve paths.

我今天发现,您可以使用stat命令来解析路径。

So for a directory like "~/Documents":

所以对于像“~/文档”这样的目录:

You can run this:

您可以运行:

stat -f %N ~/Documents

stat - f % N ~ /文档

To get the full path:

获得完整的路径:

/Users/me/Documents

/用户/我/文档

For symlinks, you can use the %Y format option:

对于符号链接,可以使用%Y格式选项:

stat -f %Y example_symlink

stat - f % Y example_symlink

Which might return a result like:

这可能会返回一个结果:

/usr/local/sbin/example_symlink

/usr/local/sbin/example_symlink

The formatting options might be different on other versions of *NIX but these worked for me on OSX.

在其他版本的*NIX上,格式化选项可能有所不同,但这些在OSX上为我工作。

#20


-3  

A simple solution using node.js:

使用node.js的简单解决方案:

#!/usr/bin/env node
process.stdout.write(require('path').resolve(process.argv[2]));

#1


145  

if you're wanting to chomp part of a filename from the path, "dirname" and "basename" are your friends, and "realpath" is handy too.

如果您想要从路径中删除文件名,“dirname”和“basename”是您的朋友,“realpath”也很方便。

dirname /foo/bar/baz 
# /foo/bar 
basename /foo/bar/baz
# baz
dirname $( dirname  /foo/bar/baz  )) 
# /foo 
realpath ../foo
# ../foo: No such file or directory
realpath /tmp/../tmp/../tmp
# /tmp

Edit

编辑

Realpath appears not to be standard issue.

Realpath似乎不是标准问题。

The closest you can get with the stock standard is

你最接近股票标准的是。

readlink -f  /path/here/.. 

Realpath appears to come from debian, and is not part of coreutils: http://packages.debian.org/unstable/utils/realpath Which was originally part of the DWWW package.

Realpath似乎来自debian,它不是coreutils的一部分:http://packages.debian.org/unstable/utils/realpath,它最初是DWWW包的一部分。

( also available on gentoo as app-admin/realpath )

(也可用于gentoo as app-admin/realpath)

readlink -m /path/there/../../ 

Works the same as

一样的工作

 realpath -s /path/here/../../

in that it doesn't need the path to actually exist to normalise it.

在这一点上,它不需要路径来实现它的正常化。

#2


80  

I don't know if there is a direct bash command to do this, but I usually do

我不知道是否有直接的bash命令来执行这个操作,但我通常会这样做。

normalDir="`cd "${dirToNormalize}";pwd`"
echo "${normalDir}"

and it works well.

它工作得很好。

#3


51  

Try realpath. Below is the source in its entirety, hereby donated to the public domain.

realpath试试。下面是它的全部来源,特此捐赠给公共领域。

// realpath.c: display the absolute path to a file or directory.
// Adam Liss, August, 2007
// This program is provided "as-is" to the public domain, without express or
// implied warranty, for any non-profit use, provided this notice is maintained.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libgen.h>   
#include <limits.h>

static char *s_pMyName;
void usage(void);

int main(int argc, char *argv[])
{
    char
        sPath[PATH_MAX];


    s_pMyName = strdup(basename(argv[0]));

    if (argc < 2)
        usage();

    printf("%s\n", realpath(argv[1], sPath));
    return 0;
}    

void usage(void)
{
    fprintf(stderr, "usage: %s PATH\n", s_pMyName);
    exit(1);
}

#4


35  

Use the readlink utility from the coreutils package.

使用coreutils包中的readlink实用程序。

MY_PATH=$(readlink -f "$0")

#5


30  

A portable and reliable solution is to use python, which is preinstalled pretty much everywhere (including Darwin). You have two options:

一个可移植和可靠的解决方案是使用python,它在任何地方都是预先安装的(包括Darwin)。你有两个选择:

  1. abspath returns an absolute path but does not resolve symlinks:

    abspath返回一个绝对路径,但不解析符号链接:

    python -c "import os,sys; print os.path.abspath(sys.argv[1])" path/to/file

    python - c”导入系统,系统;打印os.path.abspath(sys.argv[1])”路径/ /文件

  2. realpath returns an absolute path and in doing so resolves symlinks, generating a canonical path:

    realpath返回一个绝对路径,并在此过程中解析符号链接,生成规范路径:

    python -c "import os,sys; print os.path.realpath(sys.argv[1])" path/to/file

    python - c”导入系统,系统;打印os.path.realpath(sys.argv[1])”路径/ /文件

In each case, path/to/file can be either a relative or absolute path.

在每种情况下,path/to/文件都可以是相对的,也可以是绝对路径。

#6


13  

readlink is the bash standard for obtaining the absolute path. It also has the advantage of returning empty strings if paths or a path doesn't exist (given the flags to do so).

readlink是获取绝对路径的bash标准。它还具有返回空字符串的优势,如果路径或路径不存在(给定标记这样做)。

To get the absolute path to a directory that may or may not exist, but who's parents do exist, use:

为了获得一个目录的绝对路径,该目录可能存在,也可能不存在,但是谁的父母确实存在,使用:

abspath=$(readlink -f $path)

To get the absolute path to a directory that must exist along with all parents:

要获得与所有父母同时存在的目录的绝对路径:

abspath=$(readlink -e $path)

To canonicalise the given path and follow symlinks if they happen to exist, but otherwise ignore missing directories and just return the path anyway, it's:

要将给定的路径规范化,并在它们碰巧存在时遵循符号链接,但是忽略丢失的目录并返回路径,它是:

abspath=$(readlink -m $path)

The only downside is that readlink will follow links. If you do not want to follow links, you can use this alternative convention:

唯一的缺点是readlink会跟随链接。如果您不想遵循链接,您可以使用这个替代约定:

abspath=$(cd ${path%/*} && echo $PWD/${path##*/})

That will chdir to the directory part of $path and print the current directory along with the file part of $path. If it fails to chdir, you get an empty string and an error on stderr.

将chdir指向$path的目录部分,并打印当前目录以及$path的文件部分。如果它失败到chdir,就会得到一个空字符串和stderr上的错误。

#7


7  

My recent solution was:

我最近的解决方案是:

pushd foo/bar/..
dir=`pwd`
popd

Based on the answer of Tim Whitcomb.

基于Tim Whitcomb的回答。

#8


5  

As Adam Liss noted realpath is not bundled with every distribution. Which is a shame, because it is the best solution. The provided source code is great, and I will probably start using it now. Here is what I have been using until now, which I share here just for completeness:

正如Adam Liss所指出的,realpath并不是与每个发行版捆绑在一起的。这是一种耻辱,因为它是最好的解决方案。提供的源代码很好,我现在可能会开始使用它。下面是我一直在使用的,我在这里分享的只是为了完整性:

get_abs_path() {
     local PARENT_DIR=$(dirname "$1")
     cd "$PARENT_DIR"
     local ABS_PATH="$(pwd)"/"$(basename "$1")"
     cd - >/dev/null
     echo "$ABS_PATH"
} 

If you want it to resolve symlinks, just replace pwd with pwd -P.

如果您希望它解决符号链接,只需用pwd -P替换pwd。

#9


4  

Talkative, and a bit late answer. I need to write one since I'm stuck on older RHEL4/5. I handles absolute and relative links, and simplifies //, /./ and somedir/../ entries.

多话,有点晚的回答。我需要写一个,因为我被困在老的RHEL4/5。我处理绝对和相对的链接,并简化//。/和somedir / . ./条目。

test -x /usr/bin/readlink || readlink () {
        echo $(/bin/ls -l $1 | /bin/cut -d'>' -f 2)
    }


test -x /usr/bin/realpath || realpath () {
    local PATH=/bin:/usr/bin
    local inputpath=$1
    local changemade=1
    while [ $changemade -ne 0 ]
    do
        changemade=0
        local realpath=""
        local token=
        for token in ${inputpath//\// }
        do 
            case $token in
            ""|".") # noop
                ;;
            "..") # up one directory
                changemade=1
                realpath=$(dirname $realpath)
                ;;
            *)
                if [ -h $realpath/$token ] 
                then
                    changemade=1
                    target=`readlink $realpath/$token`
                    if [ "${target:0:1}" = '/' ]
                    then
                        realpath=$target
                    else
                        realpath="$realpath/$target"
                    fi
                else
                    realpath="$realpath/$token"
                fi
                ;;
            esac
        done
        inputpath=$realpath
    done
    echo $realpath
}

mkdir -p /tmp/bar
(cd /tmp ; ln -s /tmp/bar foo; ln -s ../.././usr /tmp/bar/link2usr)
echo `realpath /tmp/foo`

#10


4  

Not exactly an answer but perhaps a follow-up question (original question was not explicit):

这不是一个确切的答案,但可能是一个后续问题(最初的问题不明确):

readlink is fine if you actually want to follow symlinks. But there is also a use case for merely normalizing ./ and ../ and // sequences, which can be done purely syntactically, without canonicalizing symlinks. readlink is no good for this, and neither is realpath.

如果您想要遵循符号链接,那么readlink就很好。但也有一个用例仅仅是为了规范。/和//序列,它可以完全从语法上进行,没有规范化的符号链接。readlink对这一点没有好处,realpath也不是。

for f in $paths; do (cd $f; pwd); done

works for existing paths, but breaks for others.

对现有路径有效,但对其他路径无效。

A sed script would seem to be a good bet, except that you cannot iteratively replace sequences (/foo/bar/baz/../.. -> /foo/bar/.. -> /foo) without using something like Perl, which is not safe to assume on all systems, or using some ugly loop to compare the output of sed to its input.

一个sed脚本似乎是一个不错的选择,除非您不能迭代地替换序列(/foo/bar/baz/.. .)- > / foo / bar / . .-> /foo)不使用类似Perl的东西,这在所有系统上都是不安全的,或者使用一些丑陋的循环来比较sed的输出结果。

FWIW, a one-liner using Java (JDK 6+):

FWIW,一种使用Java (JDK 6+)的单行代码:

jrunscript -e 'for (var i = 0; i < arguments.length; i++) {println(new java.io.File(new java.io.File(arguments[i]).toURI().normalize()))}' $paths

#11


4  

I'm late to the party, but this is the solution I've crafted after reading a bunch of threads like this:

我在派对上迟到了,但这是我在阅读了一大堆这样的帖子后的解决方案:

resolve_dir() {
        (builtin cd `dirname "${1/#~/$HOME}"`'/'`basename "${1/#~/$HOME}"` 2>/dev/null; if [ $? -eq 0 ]; then pwd; fi)
}

This will resolve the absolute path of $1, play nice with ~, keep symlinks in the path where they are, and it won't mess with your directory stack. It returns the full path or nothing if it doesn't exist. It expects $1 to be a directory and will probably fail if it's not, but that's an easy check to do yourself.

这将解决$1的绝对路径,与~保持友好关系,在它们所在的路径上保持符号链接,并且不会打乱您的目录堆栈。如果它不存在,它将返回完整的路径。它期望$1是一个目录,如果不是,它很可能会失败,但是这是一个简单的检查。

#12


4  

Old question, but there is much simpler way if you are dealing with full path names at the shell level:

老问题,但是如果在shell级别上处理完整路径名,会有更简单的方法:

   abspath="$( cd "$path" && pwd )"

As the cd happens in a subshell it does not impact the main script.

当cd在子shell中发生时,它不会影响主脚本。

Two variations, supposing your shell built-in commands accept -L and -P, are:

假设您的shell内置命令接受-L和-P,两个变量是:

   abspath="$( cd -P "$path" && pwd -P )"    #physical path with resolved symlinks
   abspath="$( cd -L "$path" && pwd -L )"    #logical path preserving symlinks

Personally, I rarely need this later approach unless I'm fascinated with symbolic links for some reason.

就我个人而言,我很少需要这种后来的方法,除非我因为某种原因着迷于符号链接。

FYI: variation on obtaining the starting directory of a script which works even if the script changes it's current directory later on.

FYI:获取一个脚本的起始目录的变体,即使脚本在稍后更改它的当前目录。

name0="$(basename "$0")";                  #base name of script
dir0="$( cd "$( dirname "$0" )" && pwd )"; #absolute starting dir

The use of CD assures you always have the absolute directory, even if the script is run by commands such as ./script.sh which, without the cd/pwd, often gives just .. Useless if the script does a cd later on.

使用CD确保您始终拥有绝对目录,即使脚本是由诸如./script之类的命令运行的。没有cd/pwd,通常只提供。如果脚本稍后执行一个cd,那就没有用了。

#13


3  

Try our new Bash library product realpath-lib that we have placed on GitHub for free and unencumbered use. It's thoroughly documented and makes a great learning tool.

尝试我们的新Bash库产品realpathlib,我们已经将其放置在GitHub上,以获得免费和不受限制的使用。它被详尽地记录下来并成为一个很棒的学习工具。

It resolves local, relative and absolute paths and doesn't have any dependencies except Bash 4+; so it should work just about anywhere. It's free, clean, simple and instructive.

它解决了本地、相对和绝对路径,除了Bash 4+之外没有任何依赖关系;所以它应该在任何地方工作。它是免费的,干净的,简单的和有教育意义的。

You can do:

你能做什么:

get_realpath <absolute|relative|symlink|local file path>

This function is the core of the library:

该函数是库的核心:

function get_realpath() {

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

}

It also contains functions to get_dirname, get_filename, get_ stemname and validate_path. Try it across platforms, and help to improve it.

它还包含了get_dirname、get_filename、get_ stemname和validate_path的函数。尝试跨平台,并帮助改进它。

#14


1  

The problem with realpath is that it is not available on BSD (or OSX for that matter). Here is a simple recipe extracted from a rather old (2009) article from Linux Journal, that is quite portable:

realpath的问题是它不能在BSD(或OSX)上使用。这里有一个简单的食谱,从一个相当古老的(2009年)的Linux日志中摘录,这是非常便携的:

function normpath() {
  # Remove all /./ sequences.
  local path=${1//\/.\//\/}

  # Remove dir/.. sequences.
  while [[ $path =~ ([^/][^/]*/\.\./) ]]; do
    path=${path/${BASH_REMATCH[0]}/}
  done
  echo $path
}

Notice this variant also does not require the path to exist.

注意,这个变体也不需要路径存在。

#15


1  

Based on @Andre's answer, I might have a slightly better version, in case someone is after a loop-free, completely string-manipulation based solution. It is also useful for those who don't want to dereference any symlinks, which is the downside of using realpath or readlink -f.

基于@Andre的回答,我可能会有一个稍微好一点的版本,以防有人在一个无环的、完全基于字符串的解决方案之后。它对于那些不想取消任何符号链接的人也很有用,这是使用realpath或readlink -f的缺点。

It works on bash versions 3.2.25 and higher.

它适用于bash版本3.2.25和更高版本。

shopt -s extglob

normalise_path() {
    local path="$1"
    # get rid of /../ example: /one/../two to /two
    path="${path//\/*([!\/])\/\.\./}"
    # get rid of /./ and //* example: /one/.///two to /one/two
    path="${path//@(\/\.\/|\/+(\/))//}"
    # remove the last '/.'
    echo "${path%%/.}"
}

$ normalise_path /home/codemedic/../codemedic////.config
/home/codemedic/.config

#16


0  

Based on loveborg's excellent python snippet, I wrote this:

基于loveborg优秀的python代码片段,我写道:

#!/bin/sh

# Version of readlink that follows links to the end; good for Mac OS X

for file in "$@"; do
  while [ -h "$file" ]; do
    l=`readlink $file`
    case "$l" in
      /*) file="$l";;
      *) file=`dirname "$file"`/"$l"
    esac
  done
  #echo $file
  python -c "import os,sys; print os.path.abspath(sys.argv[1])" "$file"
done

#17


0  

FILEPATH="file.txt"
echo $(realpath $(dirname $FILEPATH))/$(basename $FILEPATH)

This works even if the file doesn't exist. It does require the directory containing the file to exist.

即使文件不存在,也可以这样做。它确实需要包含文件的目录存在。

#18


0  

I know this is an ancient question. I'm still offering an alternative. Recently I met the same issue and found no existing and portable command to do that. So I wrote the following shell script which includes a function that can do the trick.

我知道这是一个古老的问题。我还在提供另一种选择。最近我遇到了同样的问题,发现没有现成的和可移植的命令可以做到这一点。因此,我编写了下面的shell脚本,其中包含一个可以执行这个技巧的函数。

#! /bin/sh

function normalize {
  local rc=0
  local esed
  local ret

  if [ "x`uname -o | grep -i 'darwin'`" != "x" ] ; then
    esed="sed -E"
  else
    esed="sed -r"
  fi


  if [ $# -gt 0 ] ; then
    # invalid
    if [ "x`echo $1 | grep -E -e '^/\.\.'`" != "x" ] ; then
      echo $1
      return -1
    fi

    # convert to absolute path
    if [ "x`echo $1 | grep -E -e '^\/'`" == "x" ] ; then
      normalize "`pwd`/$1"
      return $?
    fi

    ret=`echo $1 | $esed 's;/\.($|/);/;g' | $esed 's;/[^/]*[^/.]+[^/]*/\.\.($|/);/;g'`
  else
    read line
    normalize "$line"
    return $?
  fi

  if [ "x`echo $ret | grep -E -e '/\.(/|$)'`" != "x" ] ; then
    ret=`normalize "$ret"`
    rc=$?
  fi


  if [ "x`echo $ret | grep -E -e '/\.\.(/|$)'`" != "x" ] ; then
    ret=`normalize "$ret"`
    rc=$?
  fi

  echo "$ret"
  return $rc
}

https://gist.github.com/bestofsong/8830bdf3e5eb9461d27313c3c282868c

https://gist.github.com/bestofsong/8830bdf3e5eb9461d27313c3c282868c

#19


-1  

I discovered today that you can use the stat command to resolve paths.

我今天发现,您可以使用stat命令来解析路径。

So for a directory like "~/Documents":

所以对于像“~/文档”这样的目录:

You can run this:

您可以运行:

stat -f %N ~/Documents

stat - f % N ~ /文档

To get the full path:

获得完整的路径:

/Users/me/Documents

/用户/我/文档

For symlinks, you can use the %Y format option:

对于符号链接,可以使用%Y格式选项:

stat -f %Y example_symlink

stat - f % Y example_symlink

Which might return a result like:

这可能会返回一个结果:

/usr/local/sbin/example_symlink

/usr/local/sbin/example_symlink

The formatting options might be different on other versions of *NIX but these worked for me on OSX.

在其他版本的*NIX上,格式化选项可能有所不同,但这些在OSX上为我工作。

#20


-3  

A simple solution using node.js:

使用node.js的简单解决方案:

#!/usr/bin/env node
process.stdout.write(require('path').resolve(process.argv[2]));