bash - working - shell script location directory




從內部獲取Bash腳本的源目錄 (20)

dirname命令是最基本的,只需解析直到$ 0(腳本名稱)變量的文件名的路徑:

dirname "$0"

但是,正如matt b指出的那樣,返回的路徑根據腳本的調用方式而有所不同。 pwd不能完成這項工作,因為它只告訴你當前目錄是什麼,而不是腳本所在的目錄。另外,如果執行了一個腳本的符號鏈接,你將獲得一個(可能是相對的)路徑到鏈接所在的位置,而不是實際的腳本。

其他一些人提到了readlink命令,但最簡單的是,您可以使用:

dirname "$(readlink -f "$0")"

readlink將腳本路徑解析為文件系統根目錄的絕對路徑。 因此,任何包含單點或雙點,波浪線和/或符號鏈接的路徑都將被解析為完整路徑。

這是一個演示這些內容的腳本, whatdir.sh:

#!/bin/bash
echo "pwd: `pwd`"
echo "\$0: $0"
echo "basename: `basename $0`"
echo "dirname: `dirname $0`"
echo "dirname/readlink: $(dirname $(readlink -f $0))"

使用相對路徑在我的主目錄中運行此腳本:

>>>$ ./whatdir.sh 
pwd: /Users/phatblat
$0: ./whatdir.sh
basename: whatdir.sh
dirname: .
dirname/readlink: /Users/phatblat

同樣,但使用腳本的完整路徑:

>>>$ /Users/phatblat/whatdir.sh 
pwd: /Users/phatblat
$0: /Users/phatblat/whatdir.sh
basename: whatdir.sh
dirname: /Users/phatblat
dirname/readlink: /Users/phatblat

現在更改目錄:

>>>$ cd /tmp
>>>$ ~/whatdir.sh 
pwd: /tmp
$0: /Users/phatblat/whatdir.sh
basename: whatdir.sh
dirname: /Users/phatblat
dirname/readlink: /Users/phatblat

最後使用符號鏈接來執行腳本:

>>>$ ln -s ~/whatdir.sh whatdirlink.sh
>>>$ ./whatdirlink.sh 
pwd: /tmp
$0: ./whatdirlink.sh
basename: whatdirlink.sh
dirname: .
dirname/readlink: /Users/phatblat

如何在該腳本中獲取Bash腳本所在目錄的路徑?

例如,假設我想使用Bash腳本作為另一個應用程序的啟動器。 我想將工作目錄更改為Bash腳本所在的目錄,因此我可以對該目錄中的文件進行操作,如下所示:

$ ./application

$_值得一提,作為0美元的替代品。 如果您從bash運行腳本,則接受的答案可以縮短為:

DIR="$( dirname "$_" )"

請注意,這必須是腳本中的第一個語句。


使用dirname "$0"

#!/bin/bash
echo "The script you are running has basename `basename "$0"`, dirname `dirname "$0"`"
echo "The present working directory is `pwd`"

如果您沒有從包含它的目錄運行腳本,則單獨使用pwd將不起作用。

[[email protected] ~]$ pwd
/home/matt
[[email protected] ~]$ ./test2.sh
The script you are running has basename test2.sh, dirname .
The present working directory is /home/matt
[[email protected] ~]$ cd /tmp
[[email protected] tmp]$ ~/test2.sh
The script you are running has basename test2.sh, dirname /home/matt
The present working directory is /tmp

嘗試以下交叉兼容的解決方案:

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

as realpathreadlink命令並不總是可用(取決於操作系統),並且${BASH_SOURCE[0]}僅在bash shell中可用。

或者,您可以在bash中嘗試以下功能:

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

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

有關:


對e-satisf和3bcdnlklvc04a解決方案的略微修改在他們的回答中指出

SCRIPT_DIR=''
pushd "$(dirname "$(readlink -f "$BASH_SOURCE")")" > /dev/null && {
    SCRIPT_DIR="$PWD"
    popd > /dev/null
}    

這應該仍然適用於他們列出的所有情況。

編輯:由於konsolebox,在推送失敗后防止彈出


對於具有GNU coreutils readlink(例如linux)的系統:

$(readlink -f "$(dirname "$0")")

$0包含腳本文件名時,無需使用BASH_SOURCE


我嘗試了其中的每一個,但沒有一個有效。 一個非常接近,但有一個小蟲子,打破了它; 他們忘了用引號包裹路徑。

還有很多人認為你是從shell運行腳本所以忘記當你打開一個新的腳本它默認你的家。

試試這個目錄的大小:

/ var /沒有人/思想/關於空間存在/在目錄/名稱/這裡是你的file.text

無論您運行的方式和位置如何,這都是正確的。

#!/bin/bash
echo "pwd: `pwd`"
echo "\$0: $0"
echo "basename: `basename "$0"`"
echo "dirname: `dirname "$0"`"

因此,為了使它真正有用,這裡是如何更改到正在運行的腳本的目錄:

cd "`dirname "$0"`"

希望有所幫助


我已經比較了給出的許多答案,並提出了一些更緊湊的解決方案。 這些似乎可以處理由您最喜歡的組合產生的所有瘋狂邊緣情況:

  • 絕對路徑或相對路徑
  • 文件和目錄軟鏈接
  • 調用scriptbash scriptbash -c scriptsource script. script . script
  • 目錄和/或文件名中的空格,製表符,換行符,unicode等
  • 文件名以連字符開頭

如果您從Linux運行,似乎使用proc句柄是找到當前正在運行的腳本的完全解析源的最佳解決方案(在交互式會話中,鏈接指向相應的/dev/pts/X ):

resolved="$(readlink /proc/$$/fd/255 && echo X)" && resolved="${resolved%$'\nX'}"

這有一點點醜陋,但修復緊湊,易於理解。 我們不僅僅使用bash原語,但我沒關係,因為readlink大大簡化了任務。 echo X在變量字符串的末尾添加一個X ,這樣文件名中的任何尾隨空格都不會被吃掉,並且行尾的參數替換${VAR%X}會刪除X 因為readlink添加了自己的換行符(如果不是我們之前的readlink ,通常會在命令替換中使用它),我們也必須擺脫它。 這是使用$''引用方案最容易實現的,它允許我們使用轉義序列(如\n來表示換行符(這也是您可以輕鬆製作狡猾的命名目錄和文件的方法)。

以上內容應該包括您在Linux上查找當前運行的腳本的需求,但如果您沒有proc文件系統,或者您正試圖找到其他文件的完全解析路徑,那麼也許您可以我會發現以下代碼很有幫助。 這只是對上述單線的略微修改。 如果您正在使用奇怪的目錄/文件名,那麼使用lsreadlink檢查輸出是有用的,因為ls將輸出“簡化”路徑,替換? 對於像換行符這樣的東西。

absolute_path=$(readlink -e -- "${BASH_SOURCE[0]}" && echo x) && absolute_path=${absolute_path%?x}
dir=$(dirname -- "$absolute_path" && echo x) && dir=${dir%?x}
file=$(basename -- "$absolute_path" && echo x) && file=${file%?x}

ls -l -- "$dir/$file"
printf '$absolute_path: "%s"\n' "$absolute_path"

我認為最好的緊湊型解決方案是:

"$( cd "$( echo "${BASH_SOURCE[0]%/*}" )"; pwd )"

除了Bash之外,沒有任何依賴。使用dirnamereadlinkbasename最終會導致兼容性問題,因此如果可能的話,最好避免使用它們。


我認為這並不像其他人那樣容易實現。 pwd不起作用,因為當前目錄不一定是腳本的目錄。 $ 0並不總是有信息。 考慮以下三種調用腳本的方法。

./script

/usr/bin/script

script

在第一種和第三種方式中,$ 0沒有完整的路徑信息。 在第二和第三,pwd不起作用。 以第三種方式獲取目錄的唯一方法是遍歷路徑並找到具有正確匹配的文件。 基本上代碼必須重做操作系統的功能。

執行所要求的一種方法是僅對/ usr / share目錄中的數據進行硬編碼,並通過完整路徑引用它。 無論如何數據都不在/ usr / bin目錄中,所以這可能是要做的事情。


簡短回答:

`dirname $0`

或( preferably ):

$(dirname "$0")

這些都不適用於Finder在OS X中啟動的bash腳本 - 我最終使用:

SCRIPT_LOC="`ps -p $$ | sed /PID/d | sed s:.*/Network/:/Network/: |
sed s:.*/Volumes/:/Volumes/:`"

不漂亮,但它完成了工作。


這應該這樣做:

DIR=$(dirname "$(readlink -f "$0")")

與路徑中的符號鏈接和空格一起使用。 有關dirnamereadlink的信息,請參見手冊頁。

編輯:

從評論軌道看來它似乎不適用於Mac OS。 我不知道為什麼會這樣。 有什麼建議?


這是一個簡單,正確的方法:

actual_path=$(readlink -f "${BASH_SOURCE[0]}")
script_dir=$(dirname "$actual_path")

說明:

  • ${BASH_SOURCE[0]} - 腳本的完整路徑。 即使在腳本被提取時,這個值也是正確的,例如source <(echo 'echo $0') ${BASH_SOURCE[0]} source <(echo 'echo $0')打印bash ,而用${BASH_SOURCE[0]}替換它將打印腳本的完整路徑。 (當然,這假設您可以依賴Bash。)

  • readlink -f - 以遞歸方式解析指定路徑中的所有符號鏈接。 這是GNU擴展,在(例如)BSD系統上不可用。 如果你正在運行Mac,你可以使用Homebrew來安裝GNU coreutils並用greadlink -f取代它。

  • 當然dirname獲取路徑的父目錄。


這是特定於Linux的,但您可以使用:

SELF=$(readlink /proc/$$/fd/255)

這是符合POSIX標準的單線程:

SCRIPT_PATH=`dirname "$0"`; SCRIPT_PATH=`eval "cd \"$SCRIPT_PATH\" && pwd"`

# test
echo $SCRIPT_PATH

嗯,如果在路徑basename&dirname中只是不打算切斷它並且走路很難(如果父節點沒有導出PATH怎麼辦!)。但是,shell必須有一個打開它的腳本句柄,而在bash中句柄是#255。

SELF=`readlink /proc/$$/fd/255`

適合我。


#!/bin/sh
PRG="$0"

# need this for relative symlinks
while [ -h "$PRG" ] ; do
   PRG=`readlink "$PRG"`
done

scriptdir=`dirname "$PRG"`

DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"

是一個有用的單行程序,它將為您提供腳本的完整目錄名稱,無論它在何處被調用。

只要用於查找腳本的路徑的最後一個組件不是符號鏈接(目錄鏈接正常),它就會起作用。 如果您還想解決腳本本身的任何鏈接,您需要一個多線解決方案:

SOURCE="${BASH_SOURCE[0]}"
while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
  DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null && pwd )"
  SOURCE="$(readlink "$SOURCE")"
  [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
done
DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null && pwd )"

最後一個將適用於別名, sourcebash -c ,符號鏈接等的任意組合。

注意:如果在運行此代碼段之前cd到其他目錄,結果可能不正確! 另外,請注意$CDPATH陷阱

要了解它是如何工作的,請嘗試運行這個更詳細的表單:

#!/bin/bash

SOURCE="${BASH_SOURCE[0]}"
while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
  TARGET="$(readlink "$SOURCE")"
  if [[ $TARGET == /* ]]; then
    echo "SOURCE '$SOURCE' is an absolute symlink to '$TARGET'"
    SOURCE="$TARGET"
  else
    DIR="$( dirname "$SOURCE" )"
    echo "SOURCE '$SOURCE' is a relative symlink to '$TARGET' (relative to '$DIR')"
    SOURCE="$DIR/$TARGET" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
  fi
done
echo "SOURCE is '$SOURCE'"
RDIR="$( dirname "$SOURCE" )"
DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
if [ "$DIR" != "$RDIR" ]; then
  echo "DIR '$RDIR' resolves to '$DIR'"
fi
echo "DIR is '$DIR'"

它將打印如下:

SOURCE './scriptdir.sh' is a relative symlink to 'sym2/scriptdir.sh' (relative to '.')
SOURCE is './sym2/scriptdir.sh'
DIR './sym2' resolves to '/home/ubuntu/dotfiles/fo fo/real/real1/real2'
DIR is '/home/ubuntu/dotfiles/fo fo/real/real1/real2'

SCRIPT_DIR=$( cd ${0%/*} && pwd -P )




directory