Ottieni il nome della directory corrente (senza percorso completo) in uno script Bash


Risposte:


1116

Non è necessario il nome di base, e in particolare la necessità di una subshell che esegue pwd (che aggiunge un'operazione fork extra e costosa ); la shell può farlo internamente usando l' espansione dei parametri :

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.

Nota che se stai applicando questa tecnica in altre circostanze (no PWD, ma qualche altra variabile con un nome di directory), potresti aver bisogno di tagliare eventuali barre finali. Di seguito viene utilizzato il supporto extglob di bash per funzionare anche con più barre finali:

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"

In alternativa, senza extglob:

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

31
Qual è la differenza tra $ {PWD ## * /} e $ PWD?
Mr_Chimp,

24
@Mr_Chimp il primo è un'operazione di espansione dei parametri che taglia il resto della directory per fornire solo il nome di base. Ho un link nella mia risposta alla documentazione sull'espansione dei parametri; sentitevi liberi di seguirlo se avete domande.
Charles Duffy,

24
@stefgosselin $PWDè il nome di una variabile bash incorporata; tutti i nomi delle variabili incorporate sono in maiuscolo (per distinguerli dalle variabili locali, che devono sempre avere almeno un carattere minuscolo). result=${PWD#*/}fa non valutare a /full/path/to/directory; invece, rimuove solo il primo elemento, rendendolo path/to/directory; l'uso di due #caratteri rende il modello corrispondente avido, abbinando il maggior numero possibile di caratteri. Leggi la pagina di espansione dei parametri, collegata nella risposta, per ulteriori informazioni.
Charles Duffy,

5
Si noti che l'output da pwdnon è sempre lo stesso del valore di $PWD, a causa delle complicazioni derivanti dall'ing cdtramite collegamenti simbolici, se bash ha l' -o physicalopzione impostata e così via. Questo era particolarmente brutto nella gestione delle directory montate automaticamente, in cui la registrazione del percorso fisico anziché quello logico avrebbe prodotto un percorso che, se usato, avrebbe permesso all'automounter di smontare spontaneamente la directory che stava usando.
Alex North-Keys,

3
Bello! Qualcuno dovrebbe scrivere un libro per vecchi conchiglie Borne come me. Forse ce n'è uno là fuori? Potrebbe avere un vecchio amministratore di sistema che dice cose del tipo: "Ai nostri tempi solo noi she cshe se volevi che la chiave di backspace funzionasse, dovevi leggere l'intera sttypagina man e ci piaceva!"
Red Cricket,

335

Usa il basenameprogramma. Per il tuo caso:

% basename "$PWD"
bin

20
Non è questo lo scopo del basenameprogramma? Cosa c'è di sbagliato in questa risposta oltre a perdere le virgolette?
Nacht - Ripristina Monica il

11
@Nacht basenameè davvero un programma esterno che fa la cosa giusta, ma in esecuzione alcun programma esterno per un bash cosa può fare out-of-the-box, utilizzando solo funzionalità built-in è sciocco, incorrere impatto sulle prestazioni ( fork(), execve(), wait(), ecc) per nessuna ragione.
Charles Duffy,

3
... a parte questo, le mie obiezioni basenamequi non si applicano dirname, perché dirnameha funzionalità che ${PWD##*/}non lo fanno - trasformando una stringa senza barre ., per esempio. Pertanto, mentre l'utilizzo dirnamecome strumento esterno ha un sovraccarico di prestazioni, ha anche funzionalità che aiutano a compensare lo stesso.
Charles Duffy,

4
@LouisMaddox, un po 'di kibitzing: la functionparola chiave è incompatibile con POSIX senza aggiungere alcun valore sulla sintassi standardizzata e la mancanza di virgolette creerebbe bug nei nomi di directory con spazi. wdexec() { "./$(basename "$PWD")"; }potrebbe essere un'alternativa preferibile.
Charles Duffy,

28
L'uso sicuro basenameè meno "efficiente". Ma è probabilmente più efficiente in termini di produttività degli sviluppatori perché è facile da ricordare rispetto alla brutta sintassi bash. Quindi, se lo ricordi, fallo. Altrimenti, basenamefunziona altrettanto bene. Qualcuno ha mai dovuto migliorare le "prestazioni" di uno script bash e renderlo più efficiente?
usr

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

​​​​​


2
@jocap ... tranne per il fatto che l'uso dell'eco lì, senza citare l'argomento, è sbagliato . Se il nome della directory ha più spazi o un carattere di tabulazione, verrà compresso in un singolo spazio; se ha un carattere jolly, verrà espanso. L'uso corretto sarebbe echo "${PWD##*/}".
Charles Duffy,

9
@jocap ... inoltre, non sono sicuro che "echo" sia in realtà una parte legittima della risposta. La domanda era come ottenere la risposta, non come stampare la risposta; se l'obiettivo fosse assegnarlo a una variabile, per esempio, name=${PWD##*/}sarebbe giusto e name=$(echo "${PWD##*/}")sarebbe sbagliato (in senso inutile inefficienza).
Charles Duffy,

24

Puoi usare una combinazione di pwd e basename. Per esempio

#!/bin/bash

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

echo "$BASENAME"

exit;

18
Per favore no. I backtick creano una subshell (quindi, un'operazione fork) - e in questo caso, li stai usando due volte ! [Come ulteriore cavillo stilistico, sebbene i nomi delle variabili debbano essere minuscoli a meno che non li si esporti nell'ambiente o siano un caso speciale riservato].
Charles Duffy,

11
e come secondo quibble backtics dovrebbe essere sostituito da $ (). ancora forcelle ma più leggibili e con meno scappamenti necessari.
Jeremy Wall,

1
Per convenzione, le variabili di ambiente (PATH, EDITOR, SHELL, ...) e le variabili di shell interne (BASH_VERSION, RANDOM, ...) sono interamente capitalizzate. Tutti gli altri nomi di variabili devono essere minuscoli. Poiché i nomi delle variabili fanno distinzione tra maiuscole e minuscole, questa convenzione evita di ignorare accidentalmente le variabili ambientali e interne.
Rany Albeg Wein,

2
Dipende dalla convenzione. Si potrebbe sostenere che tutte le variabili di shell sono variabili di ambiente (a seconda della shell), e quindi tutte le variabili di shell dovrebbero essere in maiuscolo. Qualcun altro potrebbe argomentare in base all'ambito: le variabili globali sono in maiuscolo, mentre le variabili locali sono in minuscolo e questi sono tutti globali. Ancora un altro potrebbe obiettare che rendere variabili tutte maiuscole aggiunge un ulteriore livello di contrasto con i comandi che sporcano gli script della shell, che sono anche tutte parole minuscole ma significative. Posso o meno / essere d'accordo / con nessuno di questi argomenti, ma ho visto tutti e tre in uso. :)
dannysauer,

17

Che ne dici di grep:

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

non lo consiglierei perché sembra ottenere alcune informazioni sul colore mentre pwd | xargs basenameno ... probabilmente non è così importante ma l'altra risposta è più semplice e più coerente tra gli ambienti
davidhq

8

Mi piace la risposta selezionata (Charles Duffy), ma fai attenzione se ti trovi in ​​una directory con collegamento simbolico e desideri il nome della directory di destinazione. Sfortunatamente non penso che possa essere fatto in una singola espressione di espansione dei parametri, forse mi sbaglio. Questo dovrebbe funzionare:

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

Per vedere questo, un esperimento:

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

riporta "bar"

DIRNAME

Per mostrare le directory principali di un percorso (senza incorrere in un fork-exec di / usr / bin / dirname):

echo ${target_PWD%/*}

Questo ad esempio trasformerà foo / bar / baz -> foo / bar


5
Sfortunatamente, readlink -fè un'estensione GNU e quindi non disponibile sui BSD (incluso OS X).
Xiong Chiamiov,

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

Se si utilizza la shell Bourne o ${PWD##*/}non è disponibile.


4
Cordiali saluti, ${PWD##*/}è POSIX sh - ogni moderno / bin / sh (compresi trattini, ceneri, ecc.) Lo supporta; per colpire il vero Bourne su un recente box, dovresti trovarti su un sistema Solaris leggermente vecchio. Oltre a ciò - echo "$PWD"; tralasciando le virgolette si ottengono bug (se il nome della directory ha spazi, caratteri jolly, ecc.).
Charles Duffy,

1
Sì, stavo usando un vecchio sistema Solaris. Ho aggiornato il comando per utilizzare le virgolette.
anomalia

7

Questa discussione è fantastica! Ecco un altro sapore:

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

5

Sorprendentemente, nessuno ha menzionato questa alternativa che utilizza solo i comandi bash integrati:

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

Come bonus aggiuntivo puoi facilmente ottenere il nome della directory principale con:

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

Questi funzioneranno su Bash 4.3-alpha o più recenti.


sorprendentemente? scherzando, ma altri sono molto più brevi e più facili da ricordare
codewandler

Questo è piuttosto divertente = P Prima non avevo sentito parlare di $ IFS (separatore di campo interno). il mio bash era troppo vecchio, ma posso provarlo qui: jdoodle.com/test-bash-shell-script-online
Stan Kurdziel

5

Uso:

basename "$PWD"

O

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

Riporta nello spazio il separatore di nome file interno (IFS).

IFS= 

C'è uno spazio dopo l'IFS.


4
basename $(pwd)

o

echo "$(basename $(pwd))"

2
Ha bisogno di più virgolette per essere corretto - così com'è, l'output di pwdè suddiviso in stringhe ed espanso glob prima di essere passato a basename.
Charles Duffy,

Pertanto, basename "$(pwd)"sebbene sia molto inefficiente rispetto a just basename "$PWD", il che è di per inefficiente rispetto all'utilizzo di un'espansione dei parametri invece di chiamare basenameaffatto.
Charles Duffy,

4

Per i fantini di ricerca là fuori come me:

find $PWD -maxdepth 0 -printf "%f\n"

Modificare $PWDin "$PWD"per gestire correttamente nomi di directory insoliti.
Charles Duffy,

3

di solito lo uso negli script sh

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

puoi usarlo per automatizzare le cose ...


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

2

Anche sotto grep con regex funziona,

>pwd | grep -o "\w*-*$"

3
Non funziona se il nome della directory contiene caratteri "-".
daniula,

1

Usa solo:

pwd | xargs basename

o

basename "`pwd`"

2
Questo è a tutti gli effetti una versione peggiore della risposta precedentemente data dalla Arkady ( stackoverflow.com/a/1371267/14122 ): Il xargsformultion è inefficiente e buggy quando i nomi delle directory contengono ritorni a capo letterali o caratteri di citazione, e la seconda formulazione chiama il pwdcomando in una subshell, anziché recuperare lo stesso risultato tramite un'espansione variabile incorporata.
Charles Duffy,

@CharlesDuffy È comunque una risposta valida e pratica alla domanda.
bachph


0

È possibile utilizzare l'utilità basename che elimina qualsiasi prefisso che termina in / e il suffisso (se presente nella stringa) dalla stringa e stampa il risultato sull'output standard.

$basename <path-of-directory>

0

Preferisco fortemente usare gbasename, che fa parte dei coreutils GNU.


Cordiali saluti, non viene fornito con la mia distribuzione Ubuntu.
Katastic Voyage,

@KatasticVoyage, è lì su Ubuntu, è appena chiamato basenamelì. In genere viene chiamato solo gbasenamesu MacOS e altre piattaforme che altrimenti vengono fornite con un nome di base non GNU.
Charles Duffy,

-1

I seguenti comandi comporteranno la stampa della directory di lavoro corrente in uno script bash.

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

2
(1) Non sono sicuro di dove ci stiamo assicurando che l'elenco degli argomenti sia tale che $1conterrà l'attuale directory di lavoro. (2) I pushde popdnon servono a nulla qui perché qualsiasi cosa all'interno di backtick viene eseguita in una subshell - quindi non può influenzare la directory della shell madre per cominciare. (3) L'uso "$(cd "$1"; pwd)"sarebbe sia più leggibile che resiliente nei confronti dei nomi di directory con spazi bianchi.
Charles Duffy,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.