Niezawodny sposób na skrypt Bash, aby uzyskać pełną ścieżkę do siebie [duplikat]
Mam skrypt Basha, który musi znać pełną ścieżkę. Staram się znaleźć ogólnie zgodny sposób, aby to zrobić, nie kończąc na względnych lub funky wyglądających ścieżek. Muszę tylko wspierać Basha, a nie sh, csh itp.
Co znalazłem do tej pory:
Zaakceptowana odpowiedź na uzyskanie katalogu źródłowego skryptu Bash od wewnątrz adresy uzyskanie ścieżki skryptu przez
dirname $0
, co jest w porządku, ale może to zwrócić względną ścieżkę (jak.
), co jest problemem, jeśli chcesz zmienić katalogi w skrypcie, a ścieżka nadal wskazuje na katalog skryptu. / Align = "left" /Zaakceptowana odpowiedź na bash script absolute path with OS X (OS X specyficzne, ale odpowiedź działa niezależnie) daje funkcję, która będzie testować, aby sprawdzić, czy
$0
wygląda relatywnie, a jeśli tak, to pre-pend$PWD
do niego. Ale wynik może nadal zawierać bity względne (chociaż ogólnie jest absolutny) - na przykład, jeśli skrypt znajduje sięt
w katalogu/usr/bin
, a Ty jesteś w/usr
i wpisujeszbin/../bin/t
, aby go uruchomić (tak, to jest zawiłe), kończysz z/usr/bin/../bin
jako ścieżką do katalogu skryptu. Które Działa , ale...-
Rozwiązanie
readlink
na tej stronie , które wygląda tak:# 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`
Ale
readlink
nie jest POSIX i najwyraźniej rozwiązanie opiera się na GNUreadlink
, gdzie BSD nie będzie działać z jakiegoś powodu (nie mam dostępu do systemu podobnego do BSD do sprawdzenia).
Więc, różne sposoby robienia tego, ale wszystkie mają swoje zastrzeżenia.
Jaki byłby lepszy sposób? Gdzie "lepsze" oznacza:
- daje mi absolutną ścieżkę.
- usuwa funky bity, nawet gdy są wywoływane w zawiły sposób(patrz komentarz na #2 powyżej). (Np. przynajmniej umiarkowanie kanoniczne / align = "left" / )
- opiera się tylko na bash-isms lub rzeczach, które są prawie pewne, że będą na najpopularniejszych systemach * nix (GNU / Linux, BSD i systemach podobnych do BSD, takich jak OS X, itp.).
- unika wywoływania zewnętrznych programów, jeśli to możliwe(np. preferuje wbudowane Bash).
- (Updated , thanks for the heads up, wich ) It doesn 't have to resolve symlinks (in fact, I' d kind of leave them alone, but that ' s not a requirement).
23 answers
Oto co wymyśliłem (edit: plus kilka poprawek dostarczonych przez sfstewman, levigroker, Kyle Strand i Rob Kennedy ), to wydaje się w większości pasować do moich "lepszych" kryteriów:
SCRIPTPATH="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"
Ta linia SCRIPTPATH
wydaje się szczególnie okrężna, ale potrzebujemy jej raczej niż SCRIPTPATH=`pwd`
, aby prawidłowo obsługiwać spacje i dowiązania symboliczne.
Włączenie przekierowania Wyjściowego (>/dev/null 2>&1
) obsługuje rzadkie(?) przypadek, w którym cd
może wytworzyć wyjście, które / align = "center" bgcolor = "# e0ffe0 " / cesarz Chin / / align = center / (Takie jak cd
jest nadpisywany do ls
katalogu po przełączeniu na niego.)
Zauważ również, że sytuacje Ezoteryczne, takie jak wykonywanie skryptu, który w ogóle nie pochodzi z pliku w dostępnym systemie plików (co jest całkowicie możliwe), nie są tam uwzględniane (ani w żadnej z innych odpowiedzi, które widziałem).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-01-29 15:10:19
Dziwię się, że nie wspomniano tu o komendzie realpath
. Rozumiem, że jest szeroko przenośny / portowany.
Twoje początkowe rozwiązanie staje się:
SCRIPT=`realpath $0`
SCRIPTPATH=`dirname $SCRIPT`
I pozostawić dowiązania symboliczne nierozwiązane zgodnie z Twoimi preferencjami:
SCRIPT=`realpath -s $0`
SCRIPTPATH=`dirname $SCRIPT`
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-06-20 07:11:49
Najprostszym sposobem, jaki znalazłem, aby uzyskać pełną ścieżkę kanoniczną w Bash jest użycie cd
i pwd
:
ABSOLUTE_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/$(basename "${BASH_SOURCE[0]}")"
Użycie ${BASH_SOURCE[0]}
zamiast $0
powoduje takie samo zachowanie niezależnie od tego, czy skrypt jest wywoływany jako <name>
czy source <name>
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-10-31 14:11:31
Musiałem dzisiaj wrócić do tej kwestii i znalazłem Pobierz katalog źródłowy skryptu Bash z samego skryptu:
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
W linked answer jest więcej wariantów, np. w przypadku, gdy sam skrypt jest dowiązaniem symbolicznym.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-05-03 19:21:21
Użycie:
SCRIPT_PATH=$(dirname `which $0`)
which
wypisuje na standardowe wyjście pełną ścieżkę pliku wykonywalnego, który zostałby wykonany, gdy przekazany argument został wprowadzony w wierszu polecenia powłoki (co zawiera $0)
dirname
usuwa przyrostek nie-katalogowy z nazwy pliku.
W ten sposób otrzymujesz pełną ścieżkę skryptu, bez względu na to, czy ścieżka została określona, czy nie.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-10-31 21:55:54
Pobranie ścieżki bezwzględnej skryptu powłoki
Nie używa opcji -f
w readlink, dlatego powinna działać na BSD/Mac OS X.
Podpory
- źródło ./ script (wywołany przez
.
operator kropki) - absolutna Ścieżka / Ścieżka / do / skryptu
- ścieżka względna jak ./ script
- / path / dir1/../ dir2 / dir3/./ script
- wywołane z dowiązania symbolicznego
- gdy dowiązanie symboliczne jest zagnieżdżone np.)
foo->dir1/dir2/bar bar->./../doe doe->script
- gdy rozmówca zmienia nazwa skryptów
Szukam przypadków narożnych, w których ten kod nie działa . Proszę dać mi znać.
Kod
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`]"
Znany issus
Skrypt musi być gdzieś na dysku . Niech to będzie przez sieć. jeśli spróbujesz uruchomić ten skrypt z rury to nie zadziała
wget -o /dev/null -O - http://host.domain/dir/script.sh |bash
Technicznie rzecz biorąc, jest niezdefiniowany. Praktycznie rzecz biorąc, nie ma rozsądnego sposobu, aby to wykryć. (Współpro-proces nie może uzyskać dostępu do środowiska rodzic.)Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-10-31 21:51:50
Ponieważ realpath nie jest domyślnie instalowany na moim systemie Linux, działa dla mnie:
SCRIPT="$(readlink --canonicalize-existing "$0")"
SCRIPTPATH="$(dirname "$SCRIPT")"
$SCRIPT
będzie zawierać prawdziwą ścieżkę do skryptu i $SCRIPTPATH
prawdziwą ścieżkę do katalogu zawierającego skrypt.
Przed użyciem przeczytaj komentarze tej odpowiedzi .
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-10-31 21:52:42
Łatwy do odczytania? Poniżej jest alternatywa. Ignoruje dowiązania symboliczne
#!/bin/bash
currentDir=$(
cd $(dirname "$0")
pwd
)
echo -n "current "
pwd
echo script $currentDir
Odkąd kilka lat temu opublikowałem powyższą odpowiedź, rozwinąłem swoją praktykę do używania tego specyficznego paradygmatu Linuksa, który poprawnie obsługuje dowiązania symboliczne:
ORIGIN=$(dirname $(readlink -f $0))
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-03-05 23:10:51
Możesz spróbować zdefiniować następującą zmienną:
CWD="$(cd -P -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"
Lub możesz wypróbować następującą funkcję w Bash:
realpath () {
[[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}"
}
Funkcja ta przyjmuje jeden argument. Jeśli argument ma już ścieżkę bezwzględną, wypisuje ją taką, jaką jest, w przeciwnym razie wypisuje zmienną $PWD
+ argument nazwy pliku (bez prefiksu ./
).
Powiązane:
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-06-23 21:16:11
Po Prostu:
BASEDIR=$(readlink -f $0 | xargs dirname)
Fantazyjne operatory nie są potrzebne.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-10-31 21:58:48
ODPOWIEDŹ na to pytanie bardzo późno, ale używam:
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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-24 16:11:39
Umieściliśmy nasz własny produkt realpath-lib na Githubie do bezpłatnego i nieobciążonego użytku społeczności.
Bezwstydna wtyczka, ale z tą biblioteką Basha możesz:
get_realpath <absolute|relative|symlink|local file>
Ta funkcja jest rdzeniem biblioteki:
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
}
Nie wymaga żadnych zewnętrznych zależności, tylko Bash 4+. Zawiera również funkcje do get_dirname
, get_filename
, get_stemname
and validate_path validate_realpath
. Jest darmowy, czysty, prosty i dobrze udokumentowany, więc może być również używany do celów edukacyjnych i bez wątpienia można to poprawić. Spróbuj na różnych platformach.
Update: po pewnym przejrzeniu i przetestowaniu zastąpiliśmy powyższą funkcję czymś, co osiąga ten sam wynik (bez użycia dirname, tylko czysty Bash), ale z lepszą wydajnością:
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
}
Obejmuje to również ustawienie środowiska no_symlinks
, które zapewnia możliwość rozwiązywania dowiązań symbolicznych do fizycznego systemu. Domyślnie zachowuje dowiązania symboliczne nienaruszone.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-10-05 13:15:16
Bourne shell (sh
) sposób zgodny:
SCRIPT_HOME=`dirname $0 | while read a; do cd $a && pwd && break; done`
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-10-31 15:11:12
Rozważając ten problem ponownie: istnieje bardzo popularne rozwiązanie, które jest przywołane w tym wątku, który ma swoje pochodzenie TUTAJ :
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
Trzymałem się z dala od tego rozwiązania ze względu na użycie dirname - może to powodować trudności międzyplatformowe, szczególnie jeśli skrypt musi być zablokowany ze względów bezpieczeństwa. Ale jako czystą alternatywę Basha, jak o użyciu:
DIR="$( cd "$( echo "${BASH_SOURCE[0]%/*}" )" && pwd )"
Czy to jest opcja?
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:10:48
Jeśli używamy Basha uważam, że jest to najwygodniejszy sposób, ponieważ nie wymaga wywołania żadnych zewnętrznych poleceń:
THIS_PATH="${BASH_SOURCE[0]}";
THIS_DIR=$(dirname $THIS_PATH)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-18 20:05:31
Przyjęte rozwiązanie ma niewygodne (dla mnie) nie być "zdolne do źródła":
jeśli zadzwonisz z "source ../../yourScript
", $0
byłoby "bash
"!
Następująca funkcja (dla bash >= 3.0) daje mi właściwą ścieżkę, jednak skrypt może być wywołany (bezpośrednio lub poprzez source
, ze ścieżką bezwzględną lub względną):
(przez "właściwą ścieżkę" mam na myśli pełną ścieżkę bezwzględną wywołanego skryptu, nawet gdy wywołany jest z innej ścieżki, bezpośrednio lub z "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}
Kluczem jest wykrycie przypadku" source
" i użycie ${BASH_SOURCE[0]}
, aby odzyskać rzeczywisty skrypt.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-06-24 02:45:06
One liner
`dirname $(realpath $0)`
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-12-18 02:02:32
Być może przyjęta odpowiedź na poniższe pytanie może być pomocna.
Jak mogę sprawdzić zachowanie READLINK-F GNU na komputerze Mac?
Biorąc pod uwagę, że po prostu chcesz kanonikować nazwę, którą otrzymujesz od konkatenacji $PWD
i $0
(zakładając, że $0
nie jest absolutna na początku), po prostu użyj serii zamienników regex wzdłuż linii abs_dir=${abs_dir//\/.\//\/}
i takich.
Tak, Wiem, że wygląda okropnie, ale zadziała i jest czystym Bashem.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-08-27 12:57:46
Spróbuj tego:
cd $(dirname $([ -L $0 ] && readlink -f $0 || echo $0))
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-08-29 03:05:05
Do diabła z tym zrobiłem trochę hakowania na skrypcie, który robi rzeczy czysto tekstowo, czysto w Bash. Mam nadzieję, że złapałem wszystkie przypadki edge ' a.
Zauważ, że ${var//pat/repl}
, o którym wspomniałem w drugiej odpowiedzi, nie działa, ponieważ nie można go zastąpić tylko najkrótszym możliwym dopasowaniem, co jest problemem dla zastąpienia /foo/../
, ponieważ np. /*/../
weźmie wszystko przed nim, a nie tylko pojedynczy wpis. A ponieważ te wzorce nie są tak naprawdę wyrażeniami regularnymi, nie widzę, jak można to zrobić praca. Oto ładnie zawiłe rozwiązanie, które wymyśliłem, baw się dobrze. ;)
#!/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/"
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-10-31 21:48:12
Przez jakiś czas używałem z powodzeniem następującego podejścia (nie na OS X), i używa tylko wbudowanej powłoki i obsługuje 'source foobar.sh / align = "left" /
Jeden problem z poniższym (pośpiesznie złożonym) przykładowym kodem jest taki, że funkcja używa $PWD, która może, ale nie musi być poprawna w czasie wywołania funkcji. Więc trzeba się tym zająć.
#!/bin/bash
function canonical_path() {
# Handle relative 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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-10-31 22:02:51
Yet another way to do this:
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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-12-19 14:02:50
Prościej, to jest to, co działa dla mnie:
MY_DIR=`dirname $0`
source $MY_DIR/_inc_db.sh
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-15 11:31:48