在bash中展开可能的相对路径


82

作为我的脚本的参数,有一些文件路径。这些当然可以是相对的(或包含〜)。但是对于我编写的功能,我需要绝对的路径,但没有解析其符号链接。

有什么功能吗?



7
readlink解决符号链接;OP不想这么做。
基思·汤普森



1
echo $(cd some_directory && pwd),不解析符号链接,如果some_directory不存在,则失败。工作目录不受影响。或分配给变量MY_PATH=$(cd some_directory && pwd)
qeatzy

Answers:


79

MY_PATH=$(readlink -f $YOUR_ARG)将解析相对路径,例如"./""../"

也考虑一下(来源):

#!/bin/bash
dir_resolve()
{
cd "$1" 2>/dev/null || return $?  # cd to desired directory; if fail, quell any error messages but return exit status
echo "`pwd -P`" # output full, link-resolved path
}

# sample usage
if abs_path="`dir_resolve \"$1\"`"
then
echo "$1 resolves to $abs_path"
echo pwd: `pwd` # function forks subshell, so working directory outside function is not affected
else
echo "Could not reach $1"
fi

那当然很好,但是问题是解决了simlink,这将导致与用户的混淆。
laar 2011年

man pwd:“-P显示物理当前工作目录(所有符号链接解决)”,只是删除-P
andsens

7
readlink -f在OS X上获得该功能,可以使用Homebrew,然后运行:$ brew install coreutils 然后可以使用greadlink -f
jgpawletko 2015年

45

http://www.linuxquestions.org/questions/programming-9/bash-script-return-full-path-and-filename-680368/page3.html具有以下内容

function abspath {
    if [[ -d "$1" ]]
    then
        pushd "$1" >/dev/null
        pwd
        popd >/dev/null
    elif [[ -e "$1" ]]
    then
        pushd "$(dirname "$1")" >/dev/null
        echo "$(pwd)/$(basename "$1")"
        popd >/dev/null
    else
        echo "$1" does not exist! >&2
        return 127
    fi
}

使用pushd/popd进入一种pwd有用的状态。


2
可悲的是,这确实是最好的答案,还没有被投票赞成。+1可以准确回答OP的问题。他希望符号链接不被解决,这实际上是bash本地的问题,DIRSTACK仅此而已。不错的解决方案!
cptstubing13年

5
寻求标准sh兼容性时,pushd; cd <dir>; <cmd>; popd可以用代替(cd <dir>; <cmd>)
Abbafei 2013年

16

简单的一线:

function abs_path {
  (cd "$(dirname '$1')" &>/dev/null && printf "%s/%s" "$PWD" "${1##*/}")
}

用法:

function do_something {
    local file=$(abs_path $1)
    printf "Absolute path to %s: %s\n" "$1" "$file"
}
do_something $HOME/path/to/some\ where

我仍在尝试弄清楚如何完全忽略该路径是否存在(因此也可以在创建文件时使用它)。


1
这不会更改当前的工作目录吗?cd -之后我应该重设吗?
devios1

4
没必要。注意括号,它们使里面的命令在子shell中运行。子外壳程序的状态(例如工作目录)不会泄漏到其父外壳程序。在此处阅读更多信息:tldp.org/LDP/abs/html/subshel​​ls.html
andsens

1
很好的一线。但是,有一个问题:如果$ 1的扩展值没有任何斜杠(即,它是当前目录中的文件名),则$ {1%/ *}扩展为文件名本身,而cd命令失败。您可能要改用$(dirname $ 1),它将扩展为'。'。在这种情况下。
Paulo

您说得对,已更新。我有替换一些报价的问题${1##*/}basename,虽然,没有把它在一个单独的变量,用空格的路径不能正常工作。
andsens 2014年

1
@BryanP实际上,我最终对于要维护的dotfile同步器完全落伍了。它有一个clean_path()函数删除不需要的路部分,如果你的坚持到底的相对路径$PWD,并通过运行它,它应该工作得很好:github.com/andsens/homeshick/blob/...
andsens

8

这在OS X上为我完成了窍门: $(cd SOME_DIRECTORY 2> /dev/null && pwd -P)

它应该可以在任何地方工作。其他解决方案似乎太复杂了。


4

在OS X上,您可以使用

stat -f "%N" YOUR_PATH

在Linux上,您可能具有realpath可执行文件。如果没有,以下方法可能会起作用(不仅适用于链接):

readlink -c YOUR_PATH

20
OS X的答案不起作用。stat -f "%N" PATH给我完全一样的路径
莉莉·巴拉德

3

使用readlink -f <relative-path>,例如

export FULLPATH=`readlink -f ./`

2

也许这更具可读性,并且不使用子外壳并且不会更改当前目录:

dir_resolve() {
  local dir=`dirname "$1"`
  local file=`basename "$1"`
  pushd "$dir" &>/dev/null || return $? # On error, return error code
  echo "`pwd -P`/$file" # output full, link-resolved path with filename
  popd &> /dev/null
}

1

还有另一种方法。您可以在bash脚本中使用python嵌入来解析相对路径。

abs_path=$(python3 - <<END
from pathlib import Path
path = str(Path("$1").expanduser().resolve())
print(path)
END
)

0

自我编辑,我只是​​注意到OP表示他不寻找已解决的符号链接:

“但是对于我编写的功能,我需要绝对的路径,但是没有解析它们的符号链接。”

因此,猜想这毕竟与他的问题不太相符。:)

多年来,由于我经历了很多次,并且这次我需要一个可以在OSX和linux上使用的纯bash便携式版本,因此我继续写了一个:

实际版本位于此处:

https://github.com/keen99/shell-functions/tree/master/resolve_path

但出于SO的缘故,这是当前版本(我觉得它已经过良好的测试..但我愿意接受反馈!)

使它适用于纯bourne shell(sh)可能并不困难,但我没有尝试...我太喜欢$ FUNCNAME。:)

#!/bin/bash

resolve_path() {
    #I'm bash only, please!
    # usage:  resolve_path <a file or directory> 
    # follows symlinks and relative paths, returns a full real path
    #
    local owd="$PWD"
    #echo "$FUNCNAME for $1" >&2
    local opath="$1"
    local npath=""
    local obase=$(basename "$opath")
    local odir=$(dirname "$opath")
    if [[ -L "$opath" ]]
    then
    #it's a link.
    #file or directory, we want to cd into it's dir
        cd $odir
    #then extract where the link points.
        npath=$(readlink "$obase")
        #have to -L BEFORE we -f, because -f includes -L :(
        if [[ -L $npath ]]
         then
        #the link points to another symlink, so go follow that.
            resolve_path "$npath"
            #and finish out early, we're done.
            return $?
            #done
        elif [[ -f $npath ]]
        #the link points to a file.
         then
            #get the dir for the new file
            nbase=$(basename $npath)
            npath=$(dirname $npath)
            cd "$npath"
            ndir=$(pwd -P)
            retval=0
            #done
        elif [[ -d $npath ]]
         then
        #the link points to a directory.
            cd "$npath"
            ndir=$(pwd -P)
            retval=0
            #done
        else
            echo "$FUNCNAME: ERROR: unknown condition inside link!!" >&2
            echo "opath [[ $opath ]]" >&2
            echo "npath [[ $npath ]]" >&2
            return 1
        fi
    else
        if ! [[ -e "$opath" ]]
         then
            echo "$FUNCNAME: $opath: No such file or directory" >&2
            return 1
            #and break early
        elif [[ -d "$opath" ]]
         then 
            cd "$opath"
            ndir=$(pwd -P)
            retval=0
            #done
        elif [[ -f "$opath" ]]
         then
            cd $odir
            ndir=$(pwd -P)
            nbase=$(basename "$opath")
            retval=0
            #done
        else
            echo "$FUNCNAME: ERROR: unknown condition outside link!!" >&2
            echo "opath [[ $opath ]]" >&2
            return 1
        fi
    fi
    #now assemble our output
    echo -n "$ndir"
    if [[ "x${nbase:=}" != "x" ]]
     then
        echo "/$nbase"
    else 
        echo
    fi
    #now return to where we were
    cd "$owd"
    return $retval
}

这是一个经典的例子,感谢brew:

%% ls -l `which mvn`
lrwxr-xr-x  1 draistrick  502  29 Dec 17 10:50 /usr/local/bin/mvn@ -> ../Cellar/maven/3.2.3/bin/mvn

使用此函数,它将返回-real-路径:

%% cat test.sh
#!/bin/bash
. resolve_path.inc
echo
echo "relative symlinked path:"
which mvn
echo
echo "and the real path:"
resolve_path `which mvn`


%% test.sh

relative symlinked path:
/usr/local/bin/mvn

and the real path:
/usr/local/Cellar/maven/3.2.3/libexec/bin/mvn

0

如果您的操作系统支持,请使用:

realpath "./some/dir"

并在变量中使用它:

some_path="$(realpath "./some/dir")"

这将拓展您的道路。在Ubuntu和CentOS上进行了测试,您的可能不可用。一些建议使用readlink,但是readlink的文档中说:

注意realpath(1)是用于规范化功能的首选命令。

如果人们想知道为什么我引用我的变量,那是为了保留路径中的空格。这样做realpath some path会给您两个不同的路径结果。但是realpath "some path"会返回一个。引用参数ftw :)


-1

您是否必须专门使用bash?我需要这样做,并且受够了Linux和OS X之间的差异。因此,我使用PHP来提供快速而肮脏的解决方案。

#!/usr/bin/php <-- or wherever
<?php
{
   if($argc!=2)
      exit();
   $fname=$argv[1];
   if(!file_exists($fname))
      exit();
   echo realpath($fname)."\n";
}
?>

我知道这不是一个很好的解决方案,但确实可以。


如果您已经知道文件的路径或文件在当前目录中,则此操作只返回文件的路径...在PATH设置中找不到文件
G-Man
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.