我想转换/foo/bar/../ foo

是否有bash命令可以做到这一点?


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


当前回答

一种可移植且可靠的解决方案是使用python,几乎所有地方都预装了python(包括Darwin)。你有两个选择:

Abspath返回一个绝对路径,但不解析符号链接: import os,sys;打印(os.path.abspath (sys.argv[1]))“路径/ /文件 Realpath返回一个绝对路径并解析符号链接,生成一个规范路径: import os,sys;打印(os.path.realpath (sys.argv[1]))“路径/ /文件

在每种情况下,路径/to/file可以是相对路径,也可以是绝对路径。

其他回答

realpath的问题是它在BSD(或OSX)上不可用。下面是一个简单的配方,摘自Linux Journal上一篇相当古老(2009年)的文章,它是相当可移植的:

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

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

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

我不知道是否有直接的bash命令可以做到这一点,但我通常会这样做

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

而且效果很好。

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

如果你真的想遵循符号链接,Readlink是很好的。但也有一个用例仅仅是正常化。/和../和//序列,这可以纯语法地完成,而不需要规范化符号链接。Readlink做不到这一点,realpath也不行。

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

适用于现有路径,但不适用于其他路径。

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

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

正如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"
} 

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

我知道这是一个古老的问题。我仍在提供另一种选择。最近我遇到了同样的问题,并且发现没有现有的可移植命令来执行此操作。因此,我编写了下面的shell脚本,其中包括一个可以实现此功能的函数。

#! /bin/sh                                                                                                                                                

function normalize {
  local rc=0
  local ret

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

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

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

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

  echo "$ret"
  return $rc
}

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