我有一个Bash脚本,需要知道它的完整路径。我试图找到一种广泛兼容的方式来做到这一点,而不会以相对或时髦的路径结束。我只需要支持Bash,不支持sh, csh等。

到目前为止,我发现:

The accepted answer to Getting the source directory of a Bash script from within addresses getting the path of the script via dirname $0, which is fine, but that may return a relative path (like .), which is a problem if you want to change directories in the script and have the path still point to the script's directory. Still, dirname will be part of the puzzle. The accepted answer to Bash script absolute path with OS X (OS X specific, but the answer works regardless) gives a function that will test to see if $0 looks relative and if so will pre-pend $PWD to it. But the result can still have relative bits in it (although overall it's absolute) — for instance, if the script is t in the directory /usr/bin and you're in /usr and you type bin/../bin/t to run it (yes, that's convoluted), you end up with /usr/bin/../bin as the script's directory path. Which works, but... The readlink solution on this page, which looks like this: # Absolute path to this script. /home/user/bin/foo.sh SCRIPT=$(readlink -f $0) # Absolute path this script is in. /home/user/bin SCRIPTPATH=`dirname $SCRIPT` But readlink isn't POSIX and apparently the solution relies on GNU's readlink where BSD's won't work for some reason (I don't have access to a BSD-like system to check).

有很多种方法,但都有注意事项。

还有什么更好的办法呢?“更好”的意思是:

Gives me the absolute path. Takes out funky bits even when invoked in a convoluted way (see comment on #2 above). (E.g., at least moderately canonicalizes the path.) Relies only on Bash-isms or things that are almost certain to be on most popular flavors of *nix systems (GNU/Linux, BSD and BSD-like systems like OS X, etc.). Avoids calling external programs if possible (e.g., prefers Bash built-ins). (Updated, thanks for the heads up, wich) It doesn't have to resolve symlinks (in fact, I'd kind of prefer it left them alone, but that's not a requirement).


当前回答

试试这个:

cd $(dirname $([ -L $0 ] && readlink -f $0 || echo $0))

其他回答

只是为了它的地狱,我做了一些黑客在一个脚本上做的事情,纯粹的文本,纯粹的Bash。我希望我掌握了所有的边缘情况。

注意,我在另一个答案中提到的${var//pat/repl}不起作用,因为你不能让它只替换最短的匹配,这是替换/foo/的一个问题。/例如/*/../将接受它前面的所有内容,而不仅仅是一个条目。由于这些模式并不是真正的正则表达式,我不知道如何才能使其工作。这就是我想出的巧妙的解决方案,请欣赏。;)

顺便说一句,如果你发现任何未处理的边缘情况,请告诉我。

#!/bin/bash

canonicalize_path() {
  local path="$1"
  OIFS="$IFS"
  IFS=$'/'
  read -a parts < <(echo "$path")
  IFS="$OIFS"

  local i=${#parts[@]}
  local j=0
  local back=0
  local -a rev_canon
  while (($i > 0)); do
    ((i--))
    case "${parts[$i]}" in
      ""|.) ;;
      ..) ((back++));;
      *) if (($back > 0)); then
           ((back--))
         else
           rev_canon[j]="${parts[$i]}"
           ((j++))
         fi;;
    esac
  done
  while (($j > 0)); do
    ((j--))
    echo -n "/${rev_canon[$j]}"
  done
  echo
}

canonicalize_path "/.././..////../foo/./bar//foo/bar/.././bar/../foo/bar/./../..//../foo///bar/"

我很惊讶这里没有提到realpath命令。我的理解是它可以广泛移植。

你的初始解决方案是:

SCRIPT=$(realpath "$0")
SCRIPTPATH=$(dirname "$SCRIPT")

并根据您的喜好留下未解决的符号链接:

SCRIPT=$(realpath -s "$0")
SCRIPTPATH=$(dirname "$SCRIPT")

被接受的解决方案(对我来说)不方便“来源”: 如果你从“来源../..”/yourScript", $0将是"bash"!

下面的函数(对于bash >= 3.0)给出了正确的路径,但是脚本可能会被调用(直接或通过源代码,使用绝对路径或相对路径): (这里的“正确路径”指的是被调用脚本的完整绝对路径,即使是从另一个路径直接调用,也可以使用“source”)

#!/bin/bash
echo $0 executed

function bashscriptpath() {
  local _sp=$1
  local ascript="$0"
  local asp="$(dirname $0)"
  #echo "b1 asp '$asp', b1 ascript '$ascript'"
  if [[ "$asp" == "." && "$ascript" != "bash" && "$ascript" != "./.bashrc" ]] ; then asp="${BASH_SOURCE[0]%/*}"
  elif [[ "$asp" == "." && "$ascript" == "./.bashrc" ]] ; then asp=$(pwd)
  else
    if [[ "$ascript" == "bash" ]] ; then
      ascript=${BASH_SOURCE[0]}
      asp="$(dirname $ascript)"
    fi  
    #echo "b2 asp '$asp', b2 ascript '$ascript'"
    if [[ "${ascript#/}" != "$ascript" ]]; then asp=$asp ;
    elif [[ "${ascript#../}" != "$ascript" ]]; then
      asp=$(pwd)
      while [[ "${ascript#../}" != "$ascript" ]]; do
        asp=${asp%/*}
        ascript=${ascript#../}
      done
    elif [[ "${ascript#*/}" != "$ascript" ]];  then
      if [[ "$asp" == "." ]] ; then asp=$(pwd) ; else asp="$(pwd)/${asp}"; fi
    fi  
  fi  
  eval $_sp="'$asp'"
}

bashscriptpath H
export H=${H}

关键是检测“source”大小写,并使用${BASH_SOURCE[0]}返回实际的脚本。

回答这个问题很晚,但我用:

SCRIPT=$( readlink -m $( type -p ${0} ))      # Full path to script handling Symlinks
BASE_DIR=`dirname "${SCRIPT}"`                # Directory script is run in
NAME=`basename "${SCRIPT}"`                   # Actual name of script even if linked

试试这个:

cd $(dirname $([ -L $0 ] && readlink -f $0 || echo $0))