Zasadniczo muszę uruchomić skrypt ze ścieżkami związanymi z lokalizacją pliku skryptu powłoki. Jak mogę zmienić bieżący katalog na ten sam katalog, w którym znajduje się plik skryptu?
Zasadniczo muszę uruchomić skrypt ze ścieżkami związanymi z lokalizacją pliku skryptu powłoki. Jak mogę zmienić bieżący katalog na ten sam katalog, w którym znajduje się plik skryptu?
Odpowiedzi:
W Bash powinieneś otrzymać to, czego potrzebujesz:
#!/usr/bin/env bash
BASEDIR=$(dirname "$0")
echo "$BASEDIR"
readlink
(patrz odpowiedź al poniżej)
$BASH_SOURCE
w miejsce $0
, ponieważ $0
nie zawsze zawierać ścieżkę do skryptu powołano się, na przykład podczas „pozyskiwania” skryptu.
$BASH_SOURCE
jest specyficzne dla Bash, pytanie dotyczy ogólnie skryptu powłoki.
CUR_PATH=$(pwd)
lub pwd
zwróć bieżący katalog (który nie musi być katalogiem macierzystym skryptów)!
$BASH_SOURCE
, która zwraca to, czego potrzebowałem. Mój skrypt jest wywoływany z innego skryptu i $0
zwraca, .
podczas gdy $BASH_SOURCE
zwraca właściwy podkatalog (w moim przypadku scripts
).
Oryginalny post zawiera rozwiązanie (zignoruj odpowiedzi, nie dodają niczego przydatnego). Interesującą pracę wykonuje wspomniane polecenie unix readlink
z opcją -f
. Działa, gdy skrypt jest wywoływany zarówno przez ścieżkę bezwzględną, jak i względną.
Dla bash, sh, ksh:
#!/bin/bash
# Absolute path to this script, e.g. /home/user/bin/foo.sh
SCRIPT=$(readlink -f "$0")
# Absolute path this script is in, thus /home/user/bin
SCRIPTPATH=$(dirname "$SCRIPT")
echo $SCRIPTPATH
Dla tcsh, csh:
#!/bin/tcsh
# Absolute path to this script, e.g. /home/user/bin/foo.csh
set SCRIPT=`readlink -f "$0"`
# Absolute path this script is in, thus /home/user/bin
set SCRIPTPATH=`dirname "$SCRIPT"`
echo $SCRIPTPATH
Zobacz także: https://stackoverflow.com/a/246128/59087
readlink
. Dlatego zaleciłem użycie pushd / popd (wbudowane w bash).
-f
Opcja readlink
robi coś innego na OS X (Lion) i ewentualnie BSD. stackoverflow.com/questions/1055671/…
-f
nie jest w ogóle obsługiwany w OS X (od Lion); tam możesz albo zrezygnować -f
z rozwiązania co najwyżej jednego poziomu pośredni, np. pushd "$(dirname "$(readlink "$BASH_SOURCE" || echo "$BASH_SOURCE")")"
albo możesz rzucić swój własny rekursywny skrypt śledzący dowiązanie symboliczne, jak pokazano w łączonym poście.
sh /some/other/directory/script.sh)
W tym przypadku .
byłby twój pwd, a nie/some/other/directory
Powiedział to wcześniejszy komentarz do odpowiedzi, ale łatwo można przeoczyć wszystkie pozostałe odpowiedzi.
Podczas korzystania z bash:
echo this file: "$BASH_SOURCE"
echo this dir: "$(dirname "$BASH_SOURCE")"
dirname "$BASH_SOURCE"
do obsługi spacji w $ BASH_SOURCE.
"$(dirname "${BASH_SOURCE[0]}")"
Zakładając, że używasz bash
#!/bin/bash
current_dir=$(pwd)
script_dir=$(dirname $0)
echo $current_dir
echo $script_dir
Ten skrypt powinien wydrukować katalog, w którym się znajdujesz, a następnie katalog, w którym znajduje się skrypt. Na przykład, wywołując go /
z poziomu skryptu /home/mez/
, wyświetla dane wyjściowe
/
/home/mez
Pamiętaj, że przypisując zmienne z danych wyjściowych polecenia, zawiń je $(
i )
- inaczej nie uzyskasz żądanego wyniku.
Jeśli używasz bash ....
#!/bin/bash
pushd $(dirname "${0}") > /dev/null
basedir=$(pwd -L)
# Use "pwd -P" for the path without links. man bash for more info.
popd > /dev/null
echo "${basedir}"
pushd
/ popd
z cd $(dirname "${0}")
i cd -
aby to działało na innych pocisków, jeśli mają pwd -L
.
Jak sugeruje Marko:
BASEDIR=$(dirname $0)
echo $BASEDIR
Działa to, chyba że wykonasz skrypt z tego samego katalogu, w którym znajduje się skrypt, w którym to przypadku otrzymasz wartość „.”
Aby obejść ten problem, użyj:
current_dir=$(pwd)
script_dir=$(dirname $0)
if [ $script_dir = '.' ]
then
script_dir="$current_dir"
fi
Możesz teraz używać zmiennej current_dir w całym skrypcie, aby odwoływać się do katalogu skryptów. Jednak może to nadal powodować problem z dowiązaniem symbolicznym.
Najlepsza odpowiedź na to pytanie została udzielona tutaj:
Pobieranie katalogu źródłowego skryptu Bash od wewnątrz
I to jest:
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
Jednowierszowy, który da ci pełną nazwę katalogu skryptu, bez względu na to, skąd jest wywoływany.
Aby zrozumieć, jak to działa, możesz wykonać następujący skrypt:
#!/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'"
cd $(dirname $(readlink -f $0))
Zróbmy to oneliner POSIX:
a="/$0"; a=${a%/*}; a=${a#/}; a=${a:-.}; BASEDIR=$(cd "$a"; pwd)
Testowany na wielu powłokach zgodnych z Bourne'em, w tym na BSD.
O ile wiem, jestem autorem i umieszczam go w domenie publicznej. Aby uzyskać więcej informacji, zobacz: https://www.jasan.tk/posts/2017-05-11-posix_shell_dirname_replacement/
cd: too many arguments
jeśli spacje na ścieżce i zwraca $PWD
. (oczywista poprawka, ale pokazuje tylko, ile faktycznie jest krawędzi)
BASE_DIR="$(cd "$(dirname "$0")"; pwd)";
echo "BASE_DIR => $BASE_DIR"
Jeśli chcesz uzyskać rzeczywisty katalog skryptów (niezależnie od tego, czy wywołujesz skrypt za pomocą dowiązania symbolicznego, czy bezpośrednio), spróbuj:
BASEDIR=$(dirname $(realpath "$0"))
echo "$BASEDIR"
Działa to zarówno na systemie Linux, jak i macOS. Nie widziałem, żeby ktokolwiek tutaj wspominał realpath
. Nie jestem pewien, czy takie podejście ma wady.
w systemie macOS musisz zainstalować, coreutils
aby użyć realpath
. Np brew install coreutils
. :
WPROWADZENIE
Ta odpowiedź koryguje bardzo zepsutą, ale szokująco najczęściej głosowaną odpowiedź tego wątku (napisaną przez TheMarko):
#!/usr/bin/env bash
BASEDIR=$(dirname "$0")
echo "$BASEDIR"
DLACZEGO UŻYWANIE dirname „$ 0” NA WŁASNEJ DZIAŁANIE?
nazwa katalogu $ 0 będzie działać tylko wtedy, gdy użytkownik uruchomi skrypt w bardzo specyficzny sposób. Udało mi się znaleźć kilka sytuacji, w których ta odpowiedź kończy się niepowodzeniem i powoduje awarię skryptu.
Przede wszystkim zrozummy, jak działa ta odpowiedź. Robi to, robiąc katalog skryptów
dirname "$0"
$ 0 reprezentuje pierwszą część polecenia wywołującego skrypt (jest to w zasadzie polecenie wprowadzone bez argumentów:
/some/path/./script argument1 argument2
$ 0 = "/ some / path /./ script"
dirname w zasadzie znajduje ostatni / w ciągu i obcina go tam. Więc jeśli to zrobisz:
dirname /usr/bin/sha256sum
dostaniesz: / usr / bin
Ten przykład działa dobrze, ponieważ / usr / bin / sha256sum jest poprawnie sformatowaną ścieżką, ale
dirname "/some/path/./script"
nie działałby dobrze i dałby ci:
BASENAME="/some/path/." #which would crash your script if you try to use it as a path
Załóżmy, że masz ten sam katalog co skrypt i uruchamiasz go za pomocą tego polecenia
./script
0 $ w tej sytuacji to ./script, a nazwa katalogu 0 $ da:
. #or BASEDIR=".", again this will crash your script
Za pomocą:
sh script
Bez wprowadzania pełna ścieżka da również BASEDIR = "."
Korzystanie z katalogów względnych:
../some/path/./script
Podaje nazwę katalogu 0 $ z:
../some/path/.
Jeśli jesteś w katalogu / some i wywołujesz skrypt w ten sposób (zwróć uwagę na brak / na początku, ponownie ścieżka względna):
path/./script.sh
Otrzymasz tę wartość dla nazwy katalogu 0 $:
path/.
i ./path/./script (inna forma ścieżki względnej) daje:
./path/.
Jedynymi dwiema sytuacjami, w których będzie działał oparty na 0 $ , jest to, że użytkownik użyje sh lub touch, aby uruchomić skrypt, ponieważ oba przyniosą 0 $:
$0=/some/path/script
który da ci ścieżkę, której możesz użyć z dirname.
ROZWIĄZANIE
Będziesz mieć konto i wykryć każdą z wyżej wymienionych sytuacji i zastosuj poprawkę, jeśli się pojawi:
#!/bin/bash
#this script will only work in bash, make sure it's installed on your system.
#set to false to not see all the echos
debug=true
if [ "$debug" = true ]; then echo "\$0=$0";fi
#The line below detect script's parent directory. $0 is the part of the launch command that doesn't contain the arguments
BASEDIR=$(dirname "$0") #3 situations will cause dirname $0 to fail: #situation1: user launches script while in script dir ( $0=./script)
#situation2: different dir but ./ is used to launch script (ex. $0=/path_to/./script)
#situation3: different dir but relative path used to launch script
if [ "$debug" = true ]; then echo 'BASEDIR=$(dirname "$0") gives: '"$BASEDIR";fi
if [ "$BASEDIR" = "." ]; then BASEDIR="$(pwd)";fi # fix for situation1
_B2=${BASEDIR:$((${#BASEDIR}-2))}; B_=${BASEDIR::1}; B_2=${BASEDIR::2}; B_3=${BASEDIR::3} # <- bash only
if [ "$_B2" = "/." ]; then BASEDIR=${BASEDIR::$((${#BASEDIR}-1))};fi #fix for situation2 # <- bash only
if [ "$B_" != "/" ]; then #fix for situation3 #<- bash only
if [ "$B_2" = "./" ]; then
#covers ./relative_path/(./)script
if [ "$(pwd)" != "/" ]; then BASEDIR="$(pwd)/${BASEDIR:2}"; else BASEDIR="/${BASEDIR:2}";fi
else
#covers relative_path/(./)script and ../relative_path/(./)script, using ../relative_path fails if current path is a symbolic link
if [ "$(pwd)" != "/" ]; then BASEDIR="$(pwd)/$BASEDIR"; else BASEDIR="/$BASEDIR";fi
fi
fi
if [ "$debug" = true ]; then echo "fixed BASEDIR=$BASEDIR";fi
Ten linijka informuje, gdzie znajduje się skrypt powłoki, nie ma znaczenia, czy go uruchomiłeś, czy go pozyskałeś . Ponadto rozwiązuje wszelkie zaangażowane łącza symboliczne, jeśli tak jest:
dir=$(dirname $(test -L "$BASH_SOURCE" && readlink -f "$BASH_SOURCE" || echo "$BASH_SOURCE"))
Nawiasem mówiąc, przypuszczam, że używasz / bin / bash .
Tak wiele odpowiedzi, wszystkie prawdopodobne, każda z celami przeciwników i przeciwników oraz nieco różniącymi się celami (które prawdopodobnie należy podać dla każdej z nich). Oto inne rozwiązanie, które spełnia główny cel, którym jest zarówno przejrzystość, jak i działanie we wszystkich systemach, na wszystkich wersjach bash (bez założeń dotyczących wersji readlink
lub pwd
opcji bash ) i rozsądnie spełnia to, czego można się spodziewać (np. Rozwiązywanie dowiązań symbolicznych jest interesujący problem, ale zwykle nie jest tym, czego naprawdę chcesz), obsługuj przypadki krawędzi, takie jak spacje w ścieżkach itp., ignoruje wszelkie błędy i używa rozsądnego ustawienia domyślnego, jeśli występują jakiekolwiek problemy.
Każdy składnik jest przechowywany w osobnej zmiennej, z której można korzystać osobno:
# script path, filename, directory
PROG_PATH=${BASH_SOURCE[0]} # this script's name
PROG_NAME=${PROG_PATH##*/} # basename of script (strip path)
PROG_DIR="$(cd "$(dirname "${PROG_PATH:-$PWD}")" 2>/dev/null 1>&2 && pwd)"
Zainspirowany odpowiedzią blueyed
read < <(readlink -f $0 | xargs dirname)
cd $REPLY
To powinno załatwić sprawę:
echo `pwd`/`dirname $0`
Może wyglądać brzydko w zależności od tego, w jaki sposób został wywołany i cwd, ale powinien zabrać cię tam, gdzie musisz iść (lub możesz poprawić ciąg, jeśli zależy ci na tym, jak to wygląda).
`pwd`/`dirname $0`
ale może nadal zawieść w dowiązaniach symbolicznych