bash - shell當前目錄




bash腳本獲得完整路徑的可靠方法? (16)

這個問題在這裡已經有了答案:

我有一個bash腳本,需要知道它的完整路徑。 我試圖找到一個廣泛兼容的方式來做到這一點,而不會結束相對或時髦的路徑。 我只需要支持bash,而不是sh,csh等。

到目前為止,我發現了什麼:

  1. 從內部獲取Bash腳本的源目錄 ”地址的公認答案通過dirname $0獲取腳本的路徑,這很好,但是可能會返回一個相對路徑(如. ),如果你想要,這是一個問題更改腳本中的目錄並讓路徑仍指向腳本的目錄。 儘管如此, dirname仍然是謎題的一部分。

  2. 被接受的答案是“ OSX的Bash腳本絕對路徑(OS X特定,但答案無論如何)都給出了一個函數,用於測試$0是否相對,如果是,則會預先給$PWD 。 但是結果仍然可能有相對位(儘管總體上它是絕對的) - 例如,如果腳本在目錄/usr/bin並且在/usr並且您鍵入bin/../bin/t要運行它(是的,這很複雜),最終將以/usr/bin/../bin作為腳本的目錄路徑。 哪些工作 ,但...

  3. readlink解決方案如下所示:

    # 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`
    

    但是, readlink不是POSIX,顯然這個解決方案依賴於GNU的readlink ,因為BSD不會出於某種原因工作(我沒有訪問類似BSD的系統來檢查)。

所以,採取不同的方式,但他們都有自己的警告。

什麼會更好? “更好”的意思是:

  • 給我絕對的道路。
  • 即使以錯綜複雜的方式調用時也會發出時髦的位(參見上面#2的評論)。 (例如,至少適度規範化路徑。)
  • 僅依賴於bash-isms或幾乎可以肯定在* nix系統(GNU / Linux,BSD和類似BSD的系統,如OS X等)最流行的版本中的東西。
  • 如果可能的話,避免調用外部程序(例如,更喜歡bash內置插件)。
  • 更新 ,謝謝你的提醒, wich )無需解析符號鏈接(實際上,我寧願讓它們獨立,但這不是要求)。

獲取shell腳本的絕對路徑

在readlink中不使用-f選項,因此應該在bsd / mac-osx中工作

支持

  • 源./script(由.點運算符調用時)
  • 絕對路徑/路徑/到/腳本
  • 相對路徑,如./script
  • /path/dir1/../dir2/dir3/../script
  • 從符號鏈接調用時
  • 當符號鏈接嵌套時,例如foo->dir1/dir2/bar bar->./../doe doe->script
  • 調用者更改腳本名稱時

我正在尋找這種代碼無效的角落案例 。 請告訴我。

pushd . > /dev/null
SCRIPT_PATH="${BASH_SOURCE[0]}";
while([ -h "${SCRIPT_PATH}" ]); do
    cd "`dirname "${SCRIPT_PATH}"`"
    SCRIPT_PATH="$(readlink "`basename "${SCRIPT_PATH}"`")";
done
cd "`dirname "${SCRIPT_PATH}"`" > /dev/null
SCRIPT_PATH="`pwd`";
popd  > /dev/null
echo "srcipt=[${SCRIPT_PATH}]"
echo "pwd   =[`pwd`]"

已知的問題

腳本必須放在磁盤上 ,讓它通過網絡。 如果您嘗試從PIPE運行此腳本,它將無法工作

wget -o /dev/null -O - http://host.domain/dir/script.sh |bash

從技術上講,它是不確定的。
實際上,沒有一種合理的方法來檢測這一點。 (協同進程無法訪問父級的環境)


一個班輪

`dirname $(realpath $0)`

也許對以下問題的接受答案可能有幫助。

如何在Mac上獲得GNU的readlink -f的行為?

鑑於你只是想要將從$ PWD和$ 0連接得到的名稱進行規範化(假設$ 0不是絕對開始的)。只需沿著abs_dir=${abs_dir//\/.\//\/} line這一行使用一系列正則表達式替換abs_dir=${abs_dir//\/.\//\/}等等。

是的,我知道它看起來很可怕,但它會起作用,並且是純粹的bash。


僅僅為了它而已,我已經對腳本進行了一些黑客攻擊,純粹以純文本方式執行某些操作,純粹是在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/"

嘗試這個:

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

回答這個問題很晚,但我用:

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

怎麼樣使用:

SCRIPT_PATH=$(dirname `which $0`)

打印出標準輸出可執行文件的完整路徑,當在shell提示符下輸入傳入的參數時(這是$ 0包含的內容)

dirname從文件名dirname非目錄後綴

因此,無論路徑是否被指定,結果都是腳本的完整路徑。


您可以嘗試定義以下變量:

CWD="$(cd -P -- "$(dirname -- "$0")" && pwd -P)"

或者你可以在bash中嘗試下面的函數:

realpath () {
  [[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}"
}

該函數需要1個參數。 如果參數已經有絕對路徑,按原樣打印它,否則打印$PWD變量+文件名參數(不帶./前綴)。

有關:


我們已將自己的產品realpath-lib放在GitHub上,免費和無阻礙地使用社區。

無恥的插件,但與這個Bash庫你可以:

get_realpath <absolute|relative|symlink|local file>

這個功能是庫的核心:

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

}

它不需要任何外部依賴,只需Bash 4+。 還包含函數get_dirnameget_filenameget_stemname validate_path validate_realpath 。 它是免費的,乾淨的,簡單的和有據可查的,所以它也可以用於學習的目的,毫無疑問可以改進。 嘗試跨平台。

更新:經過一些審查和測試,我們已經取代了上述功能,取得了相同的結果(不使用dirname,只使用純Bash),但效率更高:

function get_realpath() {

    [[ ! -f "$1" ]] && return 1 # failure : file does not exist.
    [[ -n "$no_symlinks" ]] && local pwdp='pwd -P' || local pwdp='pwd' # do symlinks.
    echo "$( cd "$( echo "${1%/*}" )" 2>/dev/null; $pwdp )"/"${1##*/}" # echo result.
    return 0 # success

}

這還包括一個環境設置no_symlinks ,它提供了解析符號鏈接到物理系統的能力。 默認情況下,它保持符號鏈接不變。


我已經成功地使用了下面的方法(不是在OSX上),它只使用內置的shell並處理'source foobar.sh'的情況,就我所見。

下面的示例代碼的一個問題是該函數使用$ PWD,該函數在函數調用時可能是正確的,也可能不正確。 所以需要處理。

#!/bin/bash

function canonical_path() {
  # Handle realtive vs absolute path
  [ ${1:0:1} == '/' ] && x=$1 || x=$PWD/$1
  # Change to dirname of x
  cd ${x%/*}
  # Combine new pwd with basename of x
  echo $(pwd -P)/${x##*/}
  cd $OLDPWD
}

echo $(canonical_path "${BASH_SOURCE[0]}")

type [
type cd
type echo
type pwd

我發現在bash中獲得完整規範路徑的最簡單方法是使用cdpwd

ABSOLUTE_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/$(basename "${BASH_SOURCE[0]}")"

使用${BASH_SOURCE[0]}而不是$0會產生相同的行為,無論腳本是作為<name>還是source <name>調用的


接受的解決方案對我來說是不方便的,不能“源出”:
如果你從“ source ../../yourScript ”中調用它, $0就是“ bash ”!

下面的函數(對於bash> = 3.0)給了我正確的路徑,但是可以調用腳本(直接或通過source ,具有絕對路徑或相對路徑):
(通過“正確的路徑”,我指的是被調用的腳本完全絕對路徑 ,即使從另一路徑直接調用或以“ 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]}取回實際腳本。


更簡單地說,這對我來說很有用:

MY_DIR=`dirname $0`
source $MY_DIR/_inc_db.sh

由於我的Linux系統上默認情況下沒有安裝實際路徑,因此以下內容適用於我:

SCRIPT="$(readlink --canonicalize-existing "$0")"
SCRIPTPATH="$(dirname "$SCRIPT")"

$SCRIPT將包含$SCRIPT的真實文件路徑, $SCRIPTPATH包含腳本的目錄的真實路徑。

在使用之前閱讀這個答案的評論。


簡單地說: BASEDIR=$(readlink -f $0 | xargs dirname)

沒有花哨的運營商

HIH。


還有另一種方法來做到這一點:

shopt -s extglob

selfpath=$0
selfdir=${selfpath%%+([!/])}

while [[ -L "$selfpath" ]];do
  selfpath=$(readlink "$selfpath")
  if [[ ! "$selfpath" =~ ^/ ]];then
    selfpath=${selfdir}${selfpath}
  fi
  selfdir=${selfpath%%+([!/])}
done

echo $selfpath $selfdir




path