Pod warunkiem, że masz uprawnienia do wykonywania w bieżącym katalogu - lub w katalogu, z którego wykonałeś skrypt powłoki - jeśli potrzebujesz bezwzględnej ścieżki do katalogu, wszystko czego potrzebujesz cd
.
Krok 10 cd
specyfikacji
Jeśli -P
opcja jest aktywna, $PWD
zmienna środowiskowa powinna być ustawiona na ciąg znaków, który byłby wyprowadzany pwd -P
. Jeśli nie ma wystarczających uprawnień do nowego katalogu lub nadrzędnego tego katalogu, aby określić bieżący katalog roboczy, wartość $PWD
zmiennej środowiskowej jest nieokreślona.
I dalej pwd -P
Nazwa ścieżki zapisana na standardowe wyjście nie może zawierać żadnych składników, które odnoszą się do plików typu dowiązanie symboliczne. Jeśli istnieje wiele ścieżek, które pwd
narzędzie może zapisać na standardowym wyjściu, jeden zaczyna się od znaku pojedynczego / ukośnika, a jeden lub więcej zaczyna się od dwóch znaków / ukośnika, wówczas powinien zapisać nazwę ścieżki rozpoczynającą się od znaku pojedynczego / ukośnika. Nazwa ścieżki nie może zawierać żadnych niepotrzebnych / ukośników po wiodącym jednym lub dwóch / ukośniku.
Jest tak, ponieważ cd -P
musi ustawić bieżący katalog roboczy na to, co w pwd -P
przeciwnym razie powinno zostać wydrukowane, i to cd -
musi wydrukować to $OLDPWD
, co działa:
mkdir ./dir
ln -s ./dir ./ln
cd ./ln ; cd . ; cd -
WYDAJNOŚĆ
/home/mikeserv/test/ln
czekaj na to...
cd -P . ; cd . ; cd -
WYDAJNOŚĆ
/home/mikeserv/test/dir
A kiedy cd -
drukuję, drukuję $OLDPWD
. cd
ustawia się, $PWD
jak tylko jestem cd -P .
$PWD
teraz absolutną ścieżką do /
- więc nie potrzebuję żadnych innych zmiennych. I właściwie nie powinienem nawet potrzebować końcowego, .
ale jest określone zachowanie resetowania $PWD
do $HOME
w interaktywnej powłoce, gdy nie cd
jest ozdobione. Więc to dobry nawyk, aby się rozwijać.
Tak więc wykonanie powyższej czynności na ścieżce ${0%/*}
powinno być więcej niż wystarczające do zweryfikowania $0
ścieżki, ale w przypadku, gdy $0
jest to miękkie łącze, prawdopodobnie nie możesz zmienić katalogu na to, niestety.
Oto funkcja, która sobie z tym poradzi:
zpath() { cd -P . || return
_out() { printf "%s$_zdlm\n" "$PWD/${1##*/}"; }
_cd() { cd -P "$1" ; } >/dev/null 2>&1
while [ $# -gt 0 ] && _cd .
do if _cd "$1"
then _out
elif ! [ -L "$1" ] && [ -e "$1" ]
then _cd "${1%/*}"; _out "$1"
elif [ -L "$1" ]
then ( while set -- "${1%?/}"; _cd "${1%/*}"; [ -L "${1##*/}" ]
do set " $1" "$(_cd -; ls -nd -- "$1"; echo /)"
set -- "${2#*"$1" -> }"
done; _out "$1"
); else ( PS4=ERR:\ NO_SUCH_PATH; set -x; : "$1" )
fi; _cd -; shift; done
unset -f _out _cd; unset -v _zdlm
}
Stara się zrobić tyle, ile może w bieżącej powłoce - bez wywoływania podpowłoki - chociaż są wywoływane podpowłoki z powodu błędów i miękkich linków, które nie wskazują na katalogi. To zależy od powłoki kompatybilnej z POSIX i kompatybilnej z POSIX, ls
a także z czystej _function()
przestrzeni nazw. Nadal będzie działał dobrze bez tego drugiego, choć może wtedy nadpisaćunset
w takim przypadku niektóre bieżące funkcje powłoki. Zasadniczo wszystkie te zależności powinny być dość niezawodnie dostępne na maszynie uniksowej.
Wywoływany z argumentami lub bez, pierwszą rzeczą, którą robi, jest resetowany $PWD
do wartości kanonicznej - w razie potrzeby rozwiązuje wszelkie zawarte w nim linki do ich celów. Nazywany bez argumentów i to wszystko; ale wywoływany wraz z nimi, rozwiąże i kanonizuje ścieżkę dla każdego z nich lub wydrukuje wiadomośćstderr
dlaczego nie.
Ponieważ działa on głównie w bieżącej powłoce, powinien być w stanie obsłużyć listę argumentów dowolnej długości. Wyszukuje również $_zdlm
zmienną (która również unset
przechodzi, gdy jest już w toku ) i wypisuje swoją wartość C-esc natychmiast po prawej stronie każdego argumentu, po którym zawsze następuje pojedynczy \n
znak ewline.
Często zmienia katalog, ale poza ustawieniem jego wartości kanonicznej, nie ma to wpływu $PWD
, chociaż $OLDPWD
nie można w żaden sposób liczyć na to, kiedy się skończy.
Próbuje jak najszybciej zrezygnować z każdego z argumentów. Najpierw próbuje cd
się $1
. Jeśli to możliwe, wypisuje kanoniczną ścieżkę argumentu do stdout
. Jeśli nie może, sprawdza, czy $1
istnieje i nie jest linkiem miękkim. Jeśli to prawda, drukuje się.
W ten sposób obsługuje dowolny argument typu pliku, do którego powłoka ma uprawnienia do adresowania, chyba że $1
jest dowiązaniem symbolicznym, który nie wskazuje na katalog. W takim przypadku wywołuje while
pętlę w podpowłoce.
Wzywa ls
do odczytania linku. Bieżący katalog musi zostać najpierw zmieniony na wartość początkową, aby niezawodnie obsługiwać ścieżki referencyjne, a zatem w podpowłoce podstawiania poleceń funkcja:
cd -...ls...echo /
Usuwa z lewej strony danych ls
wyjściowych tak mało, jak to konieczne, aby w pełni zawierać nazwę łącza i ciąg znaków ->
. Podczas gdy na początku starał się unikać zrobić z shift
i $IFS
okazuje się, że jest to najbardziej wiarygodna metoda tak blisko, jak mogę dowiedzieć. To jest to samo, co robi biedny link_mans_read Gillesa - i jest dobrze zrobione.
Powtórzy ten proces w pętli, dopóki zwracana nazwa pliku ls
nie będzie z pewnością miękkim linkiem. W tym momencie kanonizuje tę ścieżkę jak poprzednio, a cd
następnie drukuje.
Przykładowe użycie:
zpath \
/tmp/script \ #symlink to $HOME/test/dir/script.sh
ln \ #symlink to ./dir/
ln/nl \ #symlink to ../..
/dev/fd/0 \ #currently a here-document like : dash <<\HD
/dev/fd/1 \ #(zlink) | dash
file \ #regular file
doesntexist \ #doesnt exist
/dev/disk/by-path/pci-0000:00:16.2-usb-0:3:1.0-scsi-0:0:0:0 \
/dev/./././././././null \
. ..
WYDAJNOŚĆ
/home/mikeserv/test/dir/script.sh
/home/mikeserv/test/dir/
/home/mikeserv/test/
/tmp/zshtpKRVx (deleted)
/proc/17420/fd/pipe:[1782312]
/home/mikeserv/test/file
ERR: NO_SUCH_PATH: doesntexist
/dev/sdd
/dev/null
/home/mikeserv/test/
/home/mikeserv/
A może…
ls
dir/ file file? folder/ link@ ln@ script* script3@ script4@
zdlm=\\0 zpath * | cat -A
WYDAJNOŚĆ
/home/mikeserv/test/dir/^@$
/home/mikeserv/test/file^@$
/home/mikeserv/test/file$
^@$
/home/mikeserv/test/folder/^@$
/home/mikeserv/test/file$ #'link' -> 'file\n'
^@$
/home/mikeserv/test/dir/^@$ #'ln' -> './dir'
/home/mikeserv/test/script^@$
/home/mikeserv/test/dir/script.sh^@$ #'script3' -> './dir/script.sh'
/home/mikeserv/test/dir/script.sh^@$ #'script4' -> '/tmp/script' -> ...