Niezawodny sposób na skrypt bash, aby uzyskać pełną ścieżkę do siebie? [duplikat]
To pytanie ma już odpowiedź tutaj:
- pobieranie katalogu źródłowego skryptu Bash z wewnątrz 51 odpowiedzi
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, itd.
Co znalazłem do tej pory:
Przyjęta odpowiedź na " uzyskanie katalogu źródłowego skryptu Bash z wewnątrz " dotyczy uzyskania ś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" /The accepted odpowiedź na "bash script absolute path with OSX" (OS X specyficzne, ale odpowiedź działa niezależnie) daje funkcję, która będzie testować, aby sprawdzić, czy
$0
wygląda względnie i jeśli tak, będzie 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
to nie POSIX i najwyraźniej rozwiązanie opiera się nareadlink
GNU, gdzie BSD nie będzie działać z jakiegoś powodu (nie mam dostępu do systemu podobnego do BSD do sprawdzenia).
Jaki byłby lepszy sposób? Gdzie "lepsze" oznacza:
- daje mi absolutna ścieżka.
- usuwa funky bity, nawet gdy są wywoływane w zawiły sposób(patrz komentarz na #2 powyżej). (Np. przynajmniej umiarkowanie kanoniczna ścieżka.)
- 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 ) nie musi rozwiązywać dowiązań symbolicznych (w rzeczywistości wolałbym zostawić je w spokoju, ale to nie jest wymóg).
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")" ; 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.
Zauważ również, że sytuacje Ezoteryczne, takie jak wykonywanie skryptu, który nie pochodzi z pliku w dostępnym systemie plików w ogóle (co jest całkowicie możliwe), nie jest tam zaspokajane (lub w żadnej z innych odpowiedzi 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
2017-11-07 06:40:00
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
2016-02-18 19:41:47
Musiałem dzisiaj wrócić do tego problemu i znalazłem https://stackoverflow.com/a/246128/1034080 . opracowuje rozwiązanie, które używałem również w przeszłości .
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
2017-05-23 12:10:48
Get absolute path of shell script
Nie używa opcji -f
w readlink, dlatego powinna działać w bsd / mac-osx
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 wywołujący zmienia Skrypty nazwa
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`]"
Znane issuse
Skrypt musi być gdzieś na dysku , niech 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ć. (co-process nie może uzyskać dostępu do ENV rodzica)
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-11-29 18:10:34
A co z używaniem:
SCRIPT_PATH=$(dirname `which $0`)
which
wypisuje na stdout pełną ścieżkę pliku wykonywalnego, który byłby wykonywany, gdy przekazany argument został wprowadzony w wierszu polecenia powłoki (co zawiera $0)
dirname
usuwa przyrostek nie-katalogowy z nazwy pliku
Stąd to, co kończy się z pełną ścieżką do 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
2014-06-04 18:21:05
Ponieważ realpath nie jest domyślnie zainstalowany 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
2017-05-23 12:02: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
Możesz spróbować zdefiniować następującą zmienną:
CWD="$(cd -P -- "$(dirname -- "$0")" && pwd -P)"
Lub możesz wypróbować następującą funkcję w bash:
realpath () {
[[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}"
}
Funkcja ta przyjmuje 1 argument. Jeśli argument ma już ścieżkę bezwzględną, wypisuje ją taką jaka 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
2017-05-23 12:18:23
Sh:
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
2016-11-11 18:48:34
Po prostu: BASEDIR=$(readlink -f $0 | xargs dirname)
Brak fantazyjnych operatorów
HIH.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-12-03 13:59:03
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ę otrzymaną z połączenia $ PWD i $0 (zakładając, że $0 nie jest absolutne na początek), 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
2017-05-23 10:31:37
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
Łatwy do odczytania? alternatywa. Ignoruje dowiązania symboliczne
#!/bin/bash
currentDir=$(
cd $(dirname "$0")
pwd
)
echo -n "current "
pwd
echo script $currentDir
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-05-12 18:43:39
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
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
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ć. Więc 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
2011-01-23 14:53:44
Od jakiegoś czasu używam z powodzeniem następującego podejścia (nie na OSX) i używa tylko wbudowanej powłoki i obsługuje 'source foobar.sh / align = "left" /
Jeden problem z poniższym (pospiesznie złożonym) przykładowym kodem jest taki, że funkcja używa zmiennej $ PWD, która może, ale nie musi być poprawna w momencie wywołania funkcji. Więc trzeba się tym zająć.
#!/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
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 21:38:57
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
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
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