Jak podzielić zmienną $ 0, aby znaleźć katalog i ścieżki względne w bash?

10

Zmienna $ 0 zawiera informacje o ścieżce skryptu.

  • Jak mogę zmienić informacje o ścieżce na ścieżkę bezwzględną? Mam na myśli, jak przetwarzać ~,., .. lub podobny?
  • Jak mogę podzielić informacje o ścieżce na katalog i nazwę pliku?

Mogę do tego użyć Pythona / Perla, ale jeśli to możliwe, chcę użyć bash.

prosseek
źródło
Co próbujesz osiągnąć? btw. wywołanie skryptu za pomocą ~ / foo.sh spowoduje rozwinięcie ~ do mojego katalogu domowego za 0 $ z moją wersją bash (GNU bash, wersja 4.1.5 (2) -release- (x86_64-unknown-linux-gnu))
echox

Odpowiedzi:

17

Nie musisz przetwarzać takich rzeczy, jak ~powłoka, robi to za Ciebie. Dlatego możesz przejść ~/filenamedo dowolnego skryptu lub programu i to działa - wszystkie te programy nie radzą ~sobie same, twoja powłoka konwertuje argument /home/username/filenamei przekazuje go do programu:

$ echo ~/filename
/home/mrozekma/filename

Jeśli potrzebujesz kanonicznej nazwy pliku (takiej, która nie zawiera rzeczy takich jak ..), użyj realpath(dzięki Neil ):

$ realpath ~/../filename
/home/filename

Aby podzielić ścieżkę na nazwę katalogu i nazwę pliku, użyj dirnamei basename:

$ dirname /foo/bar/baz
/foo/bar

$ basename /foo/bar/baz
baz
Michał Mrożek
źródło
Myślę, że masz na myśli realpath, a nie readlink. readlink nie będzie działał w typowej sytuacji, tj. gdy link używa ścieżki względnej i nie jesteś w tym samym katalogu, co link.
Neil Mayhew,
@ Neil realpathjest lepszy, ale readlinkwydaje się, że działa, gdy go wypróbuję; znasz przykład, w którym zawodzi?
Michael Mrozek
1
cd /tmp; touch foo; ln -s foo bar; cd; readlink /tmp/bar. To zwraca fooi realpathzwraca /tmp/foo, i to jest to, czego chcesz.
Neil Mayhew
1
@Neil, @Michael: readlink -f(uwaga -f) jest prawie równoważne realpathi bardziej przenośne: readlink -fw jądrach GNU i istnieje również w kilku innych systemach; realpathjest pakowany osobno i może nie zostać zainstalowany (np. w Ubuntu 10.04 lub Debian Lenny zależy od niego tylko 5 nietypowych pakietów).
Gilles 'SO - przestań być zły'
@Gilles: dobra uwaga. Dzięki za przypomnienie o realpath -f.
Neil Mayhew
5

Używanie dirname i basename jak wspomniane przez Michaela powinno być najbezpieczniejszym sposobem na zdobycie tego, czego chcesz.

W każdym razie, jeśli naprawdę chcesz to zrobić za pomocą „tylko narzędzi bash”, możesz użyć podstawiania parametrów:

echo `basename $PWD`        # Basename of current working directory.
echo "${PWD##*/}"           # Basename of current working directory.
echo
echo `basename $0`          # Name of script.
echo $0                     # Name of script.
echo "${0##*/}"             # Name of script.
echo
filename=test.data
echo "${filename##*.}"      # data
                            # Extension of filename.

Ten przykład pochodzi bezpośrednio z Advanced Bash Scripting Guide, który jest wart obejrzenia.

Wyjaśnienie jest dość proste:

$ {var # Pattern} Usuń z $ var najkrótszą część $ Pattern pasującą do interfejsu $ var. $ {var ## Pattern} Usuń z $ var najdłuższą część $ Pattern pasującą do interfejsu $ var.

Spójrz na ten wzorzec jak jakieś wyrażenie regularne i / #lub ##jak jakiś chciwy / niechciany modyfikator.

Może się to przydać, jeśli będziesz musiał wykonać bardziej skomplikowane wypakowanie części ścieżki.

echox
źródło
5
Uważaj podczas korzystania ${...##...}i znajomych, a nie dirnamei basenameże nie działają we wszystkich przypadkach. Na przykład zarówno ${0##*/}i ${0%/*}rozwinąć do tego samego, $0jakby $0nie zawierało /.
Gilles „SO- przestań być zły”
@Gilles Rozumiem, dlaczego ${0%/*}nie jest dobrą alternatywą dla dirname. Ale co jest złego w używaniu ${0##*/}zamiast basename?
toxalot
@toxalot W tym przypadku jedynym problemem jest to, kiedy $0nazwa katalogu ma końcowy /. Ale żałuję mojej rady, ponieważ dirnamenie ma nic lepszego.
Gilles „SO- przestań być zły”
2

realpath to polecenie, które mówi prawdziwą ścieżkę (usuwa ... i dowiązania symboliczne itp.)

Jest to standard w FreeBSD. Według tej dyskusji jest on również dostępny dla systemu Linux:

http://www.unix.com/shell-programming-scripting/89294-geting-real-path.html

Ta dyskusja oferuje również bash:

$ bash -c "cd /foo/../bar/ ; pwd"

źródło
1
Dokładnie, coś takiego zwykle działa: MYDIR = "$ (cd $ (nazwa katalogu" $ 0 "); pwd)"
Kevin Cantu