Uzyskaj bieżącą nazwę katalogu (bez pełnej ścieżki) w skrypcie Bash


Odpowiedzi:


1115

Nie ma potrzeby używania nazwy basename, a zwłaszcza podpowłoki z pwd (co dodaje dodatkową i kosztowną operację wideł ); powłoka może to zrobić wewnętrznie za pomocą rozszerzenia parametrów :

result=${PWD##*/}          # to assign to a variable

printf '%s\n' "${PWD##*/}" # to print to stdout
                           # ...more robust than echo for unusual names
                           #    (consider a directory named -e or -n)

printf '%q\n' "${PWD##*/}" # to print to stdout, quoted for use as shell input
                           # ...useful to make hidden characters readable.

Zauważ, że jeśli stosujesz tę technikę w innych okolicznościach (nie PWD, ale jakaś inna zmienna zawierająca nazwę katalogu), może być konieczne przycięcie końcowych ukośników. Poniżej używa obsługi extglob bash do pracy nawet z wieloma końcowymi ukośnikami:

dirname=/path/to/somewhere//
shopt -s extglob           # enable +(...) glob syntax
result=${dirname%%+(/)}    # trim however many trailing slashes exist
result=${result##*/}       # remove everything before the last / that still remains
printf '%s\n' "$result"

Alternatywnie bez extglob:

dirname="/path/to/somewhere//"
result="${dirname%"${dirname##*[!/]}"}" # extglob-free multi-trailing-/ trim
result="${result##*/}"                  # remove everything before the last /

31
Jaka jest różnica między $ {PWD ## * /} a $ PWD?
Mr_Chimp

24
@Mr_Chimp to pierwsze jest operacją rozwijania parametrów, która przycina resztę katalogu, aby zapewnić tylko nazwę basename. Mam odpowiedź w dokumentacji dotyczącej rozszerzania parametrów; zachęcamy do śledzenia, jeśli masz jakieś pytania.
Charles Duffy,

24
@stefgosselin $PWDto nazwa wbudowanej zmiennej bash; wszystkie wbudowane nazwy zmiennych są pisane wielkimi literami (aby odróżnić je od zmiennych lokalnych, które zawsze powinny mieć co najmniej jedną małą literę). result=${PWD#*/}robi nie ocenia się /full/path/to/directory; zamiast tego usuwa tylko pierwszy element, czyniąc go path/to/directory; użycie dwóch #znaków sprawia, że ​​wzór pasuje do zachłannego, dopasowując tyle znaków, ile się da. Przeczytaj stronę rozwijania parametrów, połączoną w odpowiedzi, aby uzyskać więcej.
Charles Duffy,

5
Zauważ, że wynik z pwdnie zawsze jest taki sam jak wartość $PWD, ze względu na komplikacje wynikające z wejścia cdprzez dowiązania symboliczne, czy bash ma -o physicalustawioną opcję i tak dalej. Kiedyś szczególnie nieprzyjemnie obchodziło się z obsługą katalogów montowanych automatycznie, gdzie rejestrowanie ścieżki fizycznej zamiast logicznej tworzyłoby ścieżkę, która, jeśli byłaby używana, pozwoliłaby automatycznemu spontanicznemu odinstalowaniu używanego katalogu.
Alex North-Keys

3
Miły! Ktoś powinien napisać książkę dla starych facetów z Borne, takich jak ja. Może jest tam jeden? To może mieć crotchity stare sys Admin mówiąc rzeczy jak „z powrotem w moich czasach mamy tylko shi cshjeśli chciałeś klawisz Backspace do pracy trzeba było przeczytać całą sttystronę man, i podobało nam się to!”
Red Cricket

335

Skorzystaj z basenameprogramu. W twoim przypadku:

% basename "$PWD"
bin

20
Czy to nie jest celem basenameprogramu? Co jest złego w tej odpowiedzi oprócz pominięcia cytatów?
Nacht - Przywróć Monikę

11
@Nacht basenamerzeczywiście jest zewnętrzny program, który robi to, co trzeba, ale działa żadnego zewnętrznego programu do bash, co można zrobić, out-of-the-box przy użyciu tylko wbudowaną funkcjonalność jest głupie, ponosząc wpływ na wydajność ( fork(), execve(), wait(), etc) dla bez powodu.
Charles Duffy

3
... na marginesie, moje zastrzeżenia do tego basenametutaj nie dotyczą dirname, ponieważ dirnamema funkcjonalność, która ${PWD##*/}tego nie robi - na przykład przekształcanie łańcucha bez ukośników .. Tak więc, chociaż korzystanie z niego dirnamejako narzędzia zewnętrznego ma narzut związany z wydajnością, ma również funkcjonalność, która pomaga to zrekompensować.
Charles Duffy,

4
@LouisMaddox, trochę kibitzowania: Słowo functionkluczowe jest niekompatybilne z POSIX- em bez dodawania wartości ponad standardową składnię, a brak cudzysłowów powodowałby błędy w nazwach katalogów ze spacjami. wdexec() { "./$(basename "$PWD")"; }może być preferowaną alternatywą.
Charles Duffy

28
Pewne użycie basenamejest mniej „wydajne”. Ale prawdopodobnie jest bardziej wydajny pod względem produktywności programistów, ponieważ jest łatwy do zapamiętania w porównaniu do brzydkiej składni bash. Więc jeśli to pamiętasz, idź. W przeciwnym razie basenamedziała równie dobrze. Czy ktoś kiedykolwiek musiał poprawić „wydajność” skryptu bash i uczynić go bardziej wydajnym?
usr

139
$ echo "${PWD##*/}"

W pobliżu


2
@jocap ... poza tym, że użycie echa tam, bez cytowania argumentu, jest nieprawidłowe . Jeśli nazwa katalogu ma wiele spacji lub znak tabulacji, zostanie zwinięta do pojedynczej spacji; jeśli ma znak wieloznaczny, zostanie rozwinięty. Prawidłowe użycie byłoby echo "${PWD##*/}".
Charles Duffy,

9
@jocap ... też nie jestem pewien, czy „echo” jest w rzeczywistości uzasadnioną częścią odpowiedzi. Pytanie brzmiało: jak uzyskać odpowiedź, a nie jak wydrukować odpowiedź; jeśli celem name=${PWD##*/}byłoby na przykład przypisanie jej do zmiennej, byłoby to słuszne i name=$(echo "${PWD##*/}")błędne (w sensie niepotrzebnej nieefektywności).
Charles Duffy,

24

Możesz użyć kombinacji pwd i basename. Na przykład

#!/bin/bash

CURRENT=`pwd`
BASENAME=`basename "$CURRENT"`

echo "$BASENAME"

exit;

18
Proszę nie. Backticks tworzą podpowłokę (czyli operację rozwidlenia) - w tym przypadku używasz ich dwa razy ! [Jako dodatkowy, aczkolwiek stylistyczny spór - nazwy zmiennych powinny być pisane małymi literami, chyba że eksportujesz je do środowiska lub są to specjalne, zastrzeżone przypadki].
Charles Duffy,

11
i jako drugi styl quibble backtics należy zastąpić $ (). wciąż widelce, ale bardziej czytelne i mniej potrzebne.
Jeremy Wall,

1
Zgodnie z konwencją zmienne środowiskowe (PATH, EDITOR, SHELL, ...) i wewnętrzne zmienne powłoki (BASH_VERSION, RANDOM, ...) są w pełni pisane wielkimi literami. Wszystkie pozostałe nazwy zmiennych powinny być pisane małymi literami. Ponieważ w nazwach zmiennych rozróżniana jest wielkość liter, konwencja ta pozwala uniknąć przypadkowego zastąpienia zmiennych środowiskowych i wewnętrznych.
Rany Albeg Wein 21.01.16

2
Zależy od konwencji. Można argumentować, że wszystkie zmienne powłoki są zmiennymi środowiskowymi (w zależności od powłoki), a zatem wszystkie zmienne powłoki powinny być pisane wielkimi literami. Ktoś inny mógłby się spierać w oparciu o zakres - zmienne globalne są wielkimi literami, podczas gdy zmienne lokalne są małymi literami i wszystkie są globalne. Jeszcze inny mógłby argumentować, że przekształcanie zmiennych na wielkie litery dodaje dodatkową warstwę kontrastu z poleceniami zaśmiecającymi skrypty powłoki, które są również małymi, ale znaczącymi słowami. Mogę, ale nie zgadzam się / z żadnym z tych argumentów, ale widziałem wszystkie trzy używane. :)
dannysauer

17

Co powiesz na grep:

pwd | grep -o '[^/]*$'

nie poleciłbym, ponieważ wydaje się, że dostaje trochę informacji o kolorze, podczas pwd | xargs basenamegdy nie .. prawdopodobnie nie jest tak ważny, ale druga odpowiedź jest prostsza i bardziej spójna w różnych środowiskach
davidhq

8

Podoba mi się wybrana odpowiedź (Charles Duffy), ale bądź ostrożny, jeśli jesteś w dowiązanym do siebie katalogu i chcesz nazwę docelowego katalogu. Niestety nie sądzę, że można tego dokonać w wyrażeniu rozwijającym jeden parametr, być może się mylę. To powinno działać:

target_PWD=$(readlink -f .)
echo ${target_PWD##*/}

Aby to zobaczyć, eksperyment:

cd foo
ln -s . bar
echo ${PWD##*/}

zgłasza „pasek”

DIRNAME

Aby wyświetlić wiodące katalogi ścieżki (bez wywoływania fork-exec z / usr / bin / dirname):

echo ${target_PWD%/*}

To np. Przekształci foo / bar / baz -> foo / bar


5
Niestety readlink -fjest rozszerzeniem GNU, a zatem nie jest dostępne w BSD (w tym OS X).
Xiong Chiamiov

8
echo "$PWD" | sed 's!.*/!!'

Jeśli używasz powłoki Bourne'a lub ${PWD##*/}nie jest dostępny.


4
FYI, ${PWD##*/}to POSIX sh - obsługuje każdy nowoczesny / bin / sh (w tym myślnik, popiół itp.); aby trafić do rzeczywistego Bourne'a na najnowszym pudełku, musisz być w nieco starszym systemie Solaris. Poza tym - echo "$PWD"; pominięcie cudzysłowu prowadzi do błędów (jeśli nazwa katalogu zawiera spacje, znaki wieloznaczne itp.).
Charles Duffy

1
Tak, korzystałem ze starego systemu Solaris. Zaktualizowałem polecenie, aby używać cudzysłowów.
anomalny

7

Ten wątek jest świetny! Oto jeszcze jeden smak:

pwd | awk -F / '{print $NF}'

5

Zaskakujące, nikt nie wspomniał o tej alternatywie, która wykorzystuje tylko wbudowane polecenia bash:

i="$IFS";IFS='/';set -f;p=($PWD);set +f;IFS="$i";echo "${p[-1]}"

Jako dodatkowy bonus możesz łatwo uzyskać nazwę katalogu nadrzędnego za pomocą:

[ "${#p[@]}" -gt 1 ] && echo "${p[-2]}"

Będą działać na Bash 4.3-alpha lub nowszym.


zaskakująco? tylko żartuję, ale inne są znacznie krótsze i łatwiejsze do zapamiętania
programista

To dość zabawne = P Nie słyszałem wcześniej o $ IFS (separatorze pól wewnętrznych). mój bash był za stary, ale mogę go przetestować tutaj: jdoodle.com/test-bash-shell-script-online
Stan Kurdziel

5

Posługiwać się:

basename "$PWD"

LUB

IFS=/ 
var=($PWD)
echo ${var[-1]} 

Obróć wewnętrzny separator nazw plików (IFS) z powrotem do spacji.

IFS= 

Po IFS jest jedna spacja.


4
basename $(pwd)

lub

echo "$(basename $(pwd))"

2
Potrzebuje więcej cytatów, aby były poprawne - w tej chwili wynik pwdjest dzielony na ciągi i rozwijany glob przed przekazaniem do basename.
Charles Duffy,

Tak więc basename "$(pwd)"- chociaż jest to bardzo nieefektywne w porównaniu do sprawiedliwego basename "$PWD", które samo w sobie jest nieefektywne w porównaniu do używania rozszerzenia parametrów zamiast wywoływania basenamew ogóle.
Charles Duffy


3

zwykle używam tego w skryptach sh

SCRIPTSRC=`readlink -f "$0" || echo "$0"`
RUN_PATH=`dirname "${SCRIPTSRC}" || echo .`
echo "Running from ${RUN_PATH}"
...
cd ${RUN_PATH}/subfolder

możesz użyć tego do automatyzacji rzeczy ...


readlink: illegal option -- f usage: readlink [-n] [file ...]


1

Po prostu użyj:

pwd | xargs basename

lub

basename "`pwd`"

2
Jest to pod każdym względem gorsza wersja odpowiedzi poprzednio udzielonej przez Arkady'ego ( stackoverflow.com/a/1371267/14122 ): xargsFormuła jest nieefektywna i błędna, gdy nazwy katalogów zawierają dosłowne znaki nowej linii lub znaki cudzysłowu, a druga formuła wywołuje pwdpolecenie w podpowłoce zamiast pobierać ten sam wynik za pomocą wbudowanego rozszerzenia zmiennej.
Charles Duffy

@CharlesDuffy Niemniej jest to ważna i praktyczna odpowiedź na pytanie.
bachph


0

Możesz użyć narzędzia basename, które usuwa z łańcucha dowolny prefiks kończący się na / i sufiks (jeśli występuje w ciągu) i drukuje wynik na standardowym wyjściu.

$basename <path-of-directory>

0

Zdecydowanie wolę używać gbasename, który jest częścią jądra GNU.


Do Twojej wiadomości, nie pochodzi z moją dystrybucją Ubuntu.
Katastic Voyage

@KastasticVoyage, jest tam na Ubuntu, po prostu się basenametam nazywa . Jest to zwykle wywoływane tylko gbasenamena MacOS i innych platformach, które w przeciwnym razie są dostarczane z basenemame bez GNU.
Charles Duffy

-1

Następujące polecenia spowodują wydrukowanie bieżącego katalogu roboczego w skrypcie bash.

pushd .
CURRENT_DIR="`cd $1; pwd`"
popd
echo $CURRENT_DIR

2
(1) Nie jestem pewien, gdzie upewniamy się, że lista argumentów $1zawiera bieżący katalog roboczy. (2) pushdI popdnie służą tutaj, ponieważ wszystko w backstikach odbywa się w podpowłoce - więc nie może wpływać na katalog powłoki nadrzędnej na początek. (3) Używanie "$(cd "$1"; pwd)"byłoby bardziej czytelne i odporne na nazwy katalogów z białymi znakami.
Charles Duffy
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.