簡體   English   中英

一線確定當前正在運行的bash腳本的目錄

[英]One-liner to determine the directory of the currently running bash script

這個問題與以下問題有關: 從內部獲取Bash腳本的源目錄 -在該問題中,有一個完美的答案,該示例顯示show獲取當前正在運行的bash腳本的目錄(包括讀取符號鏈接的實際位置)。

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 )"

除了需要大量樣板代碼之外,這非常有效。 我正在處理一個腳本包,其中包含許多互相使用的小腳本。 此腳本包托管在git repo中,並且必須與位置無關 例如,無論克隆到哪個目錄,它都應始終以相同的方式工作。 一個人必須能夠將此倉庫克隆到許多不同的目錄中,並獨立使用它們。 許多腳本只是命令的包裝,或者它們正在同一目錄中或相對於包含目錄調用其他腳本。 例如,以下命令模擬“ mongo”命令,但在docker容器中運行該命令:

#!/bin/bash

set -e

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 )"
# mongo-container is another script that determines the container id
# and it must be referenced relatively to the containing directory
MONGOE="docker exec -ti `${DIR}/mongo-container`"
${MONGOE} mongo "$@"

此代碼的90%用於確定腳本的目錄,只有10%的代碼有用。 我大約有20個這樣的腳本。 這意味着我的bash腳本中有90%只是“確定約束目錄”而沒有做任何事情。 當我看着它們時,我幾乎看不到它們在做什么,因為有很多樣板代碼。 必須有更好的方法,我只是想不通。

有任何想法嗎?

也許您想在專用的通用GNU / Bash腳本中“分解”此源代碼?

首先,我認為您可以使用大多數GNU操作系統上可用的which命令(通常無需額外安裝)

https://savannah.gnu.org/projects/which/

然后,您可以創建一個“通用功能”文件,其中包含用於定義完整路徑的源代碼,包括符號鏈接管理,我們將其稱為/tmp/myTrueDir/common.sh

#!/bin/bash

set -e

# Usage: resolveCompleteAbsolutePath <$0 path to regard>
function resolveCompleteAbsolutePath() {
  local SOURCE="$1"
  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 )"

  echo "$DIR"
}

然后,您可以在所有小腳本的開頭使用它(首先,我們不必管理符號鏈接即可到達腳本相同目錄中的common.sh文件)

currentDir=$( dirname "$( which "$0" )" )
source "$currentDir/common.sh"

最終,您可以使用resolveCompleteAbsolutePath函數(在common.sh文件中定義)來獲取完整的路徑,包括符號鏈接解析。

這樣,您的腳本將僅包含所需的有趣源代碼,並且路徑管理將在同一位置分解。

例如,您可以使用此文件系統結構輕松測試所有這些:

/tmp/myTrueDir/
/tmp/myTrueDir/common.sh
/tmp/myTrueDir/test.sh

使用具有以下示例行的/tmp/myTrueDir/test.sh文件:

#!/bin/bash

currentDir=$( dirname "$( which "$0" )" )
source "$currentDir/common.sh"
resolvedPath=$( resolveCompleteAbsolutePath "$0" )

echo "currentDir: $currentDir"
echo "resolvedPath: $resolvedPath"

然后,您可以創建一個指向真實目錄的符號鏈接,假設:

ln -s /tmp/myTrueDir /tmp/mySymbDir

然后,您可以從符號路徑(即/tmp/mySymbDir/test.sh)中調用測試腳本,然后按需要查看其工作原理:

currentDir: /tmp/mySymbDir
resolvedDir: /tmp/myTrueDir

請參閱BashFAQ / 028(如何確定腳本的位置?...)以獲取有關此問題的良好處理。 特別要注意第二段:“重要的是要意識到,在一般情況下,此問題沒有解決方案。您可能聽說過的任何方法以及下面將詳細介紹的任何方法都存在缺陷,並且只能在以下情況下起作用:首先,請盡量不通過不依賴腳本位置來完全避免該問題!”。

可能是您的情況如此,所以可能有足夠好的解決方案。 如果您的系統具有支持-e選項的readlink ,請嘗試:

prog_realpath=$(readlink -e -- "${BASH_SOURCE[0]}")
prog_realdir=${prog_realpath%/*}/

請注意,如果程序的“真實”路徑以換行符結尾,則prog_realpath中的值將是錯誤的。 可以解決此問題,但這是極不可能的情況,並且不會阻止prog_realdir中的值正確。

有關我更改SOURCEDIR名稱的原因,請參見正確的Bash和shell腳本變量大寫

prog_realdir值上的結尾/可以確保即使程序位於根目錄中也有效。 使用dirname來獲取目錄可以避免/問題,但除非使用其他技巧,否則它很容易受到尾隨換行符問題的影響。 請參閱shell:在命令替換中保留尾隨換行符('\\ n'),以獲取有關尾隨換行符問題的更多信息。

如果您的系統不(全部)支持readlink -e ,則可以改用realpath 有關多個操作系統上readlinkrealpath可用性的更多信息,請參見“ realpath”和“ readlink -f”之間的區別

我傾向於在代碼中添加以下行

PROGDIR=$(cd $(dirname $0) && pwd)

這將更改為腳本的目錄,然后運行pwd以獲取目錄路徑。 我從來沒有這個問題。

希望這可以幫助。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM