bash - shell當前目錄
bash腳本獲得完整路徑的可靠方法? (16)
這個問題在這裡已經有了答案:
- 從 50個答案中 獲取Bash腳本的源代碼目錄
我有一個bash腳本,需要知道它的完整路徑。 我試圖找到一個廣泛兼容的方式來做到這一點,而不會結束相對或時髦的路徑。 我只需要支持bash,而不是sh,csh等。
到目前為止,我發現了什麼:
“ 從內部獲取Bash腳本的源目錄 ”地址的公認答案通過
dirname $0
獲取腳本的路徑,這很好,但是可能會返回一個相對路徑(如.
),如果你想要,這是一個問題更改腳本中的目錄並讓路徑仍指向腳本的目錄。 儘管如此,dirname
仍然是謎題的一部分。被接受的答案是“ OSX的Bash腳本絕對路徑 ” (OS X特定,但答案無論如何)都給出了一個函數,用於測試
$0
是否相對,如果是,則會預先給$PWD
。 但是結果仍然可能有相對位(儘管總體上它是絕對的) - 例如,如果腳本在目錄/usr/bin
並且在/usr
並且您鍵入bin/../bin/t
要運行它(是的,這很複雜),最終將以/usr/bin/../bin
作為腳本的目錄路徑。 哪些工作 ,但...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)`
也許對以下問題的接受答案可能有幫助。
鑑於你只是想要將從$ 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_dirname
, get_filename
, get_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中獲得完整規範路徑的最簡單方法是使用cd
和pwd
:
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