Uzyskaj ostatni katalog / nazwa pliku w argumencie ścieżki pliku w Bash

228

Próbuję napisać haczyk po zatwierdzeniu SVN, który jest hostowany na naszym serwerze programistycznym. Moim celem jest próba automatycznego pobrania kopii zatwierdzonego projektu do katalogu, w którym znajduje się on na serwerze. Jednak muszę być w stanie odczytać tylko ostatni katalog z ciągu katalogu przekazanego do skryptu, aby przejść do tego samego podkatalogu, w którym hostowane są nasze projekty.

Na przykład, jeśli dokonam zatwierdzenia SVN dla projektu „przykład”, mój skrypt otrzyma „/ usr / local / svn / repos / example” jako pierwszy argument. Muszę pobrać tylko „przykład” z końca łańcucha, a następnie połączyć go z innym łańcuchem, aby móc przejść do kasy „/ server / root / example” i natychmiast zobaczyć zmiany na żywo.

TJ L.
źródło

Odpowiedzi:

344

basename usuwa prefiks katalogu ścieżki:

$ basename /usr/local/svn/repos/example
example
$ echo "/server/root/$(basename /usr/local/svn/repos/example)"
/server/root/example
coś
źródło
2
basename jest zdecydowanie tym, czego szukam. Jak jednak uzyskać nazwę bazową argumentu przechowywaną w zmiennej? Np.SUBDIR="/path/to/whatever/$(basename $1)"
TJ L
5
@ tj111: brzmi jak nie $1, lub $1jest pusty
sth
niestety, jeśli zawijasz polecenia, basename nie jest dobrym pomysłem. tylko coś, o czym należy pamiętać
dtc
98

Aby uzyskać dowolną ścieżkę do ścieżki, można zastosować następujące podejście:

some_path=a/b/c
echo $(basename $some_path)
echo $(basename $(dirname $some_path))
echo $(basename $(dirname $(dirname $some_path)))

Wynik:

c
b
a
Jingguo Yao
źródło
1
nie działa ze ścieżkami, które mają spacje ... możesz to przezwyciężyć cytatami ... co jakoś działaecho "$(basename "$(dirname "$pathname")")"
Ray Foss
75

Bash może uzyskać ostatnią część ścieżki bez konieczności wywoływania zewnętrznego basename:

subdir="/path/to/whatever/${1##*/}"
Wstrzymano do odwołania.
źródło
2
Na moim komputerze Mac użycie notacji podciągowej jest o ponad rząd wielkości szybsze niż dirname / basename w przypadku, gdy robisz coś trywialnego dla każdego z kilku tysięcy plików.
George
1
d=/home/me/somefolder;subdir="/$d/${1##*/}"Skończyło się na tym, //home/me/somefolder//że $ d faktycznie pochodzi z pętli for d in $(find $SOMEFOLDER -maxdepth 1 -type d);Używanie subdir=$(basename $d)działa zgodnie z oczekiwaniami.
Buttle Butkus
1
@ButtleButkus: Powinieneś użyć whilezamiast foriterować wynik find( find -print0 | xargs -0jest lepszy) lub globbing: for d in $SOMEFOLDER/*/(końcowe slash działa jak -type d- możesz użyć **w Bash 4 do rekurencji, jeśli shopt -s globstar, ale komunikat „Za długa lista argumentów” to możliwy). Zauważ, że ${1}część polecenia reprezentuje pierwszy argument skryptu lub funkcji. Może być konieczne użycie ${d##*/}innej specyfikacji zmiennej lub argumentu albo upewnienie się, że argument jest przekazywany$1
wstrzymany do odwołania.
2
@DennisWilliamson Bardzo dziękuję za udostępnienie . Dla wszystkich przyszłych czytelników, którzy zaczynają się zastanawiać, dlaczego to nie działa lub jestem jedynym głupcem tutaj 😂. Powyższa odpowiedź zakłada, że $1zawiera the path from which last component is to be taken out. Tęskniłem za tą częścią. Mój przypadek użycia: target_path='/home/user/dir1/dir2/dir3/'; target_path="${target_path%/}"; last_component=${target_path##*/}; echo $last_component- Działa 😉
Vinay Vissh
2
Tutaj znajduje się wyjaśnienie, jak i dlaczego ${1##*/} działa: unix.stackexchange.com/a/171786/15070
Matthew