Récupère le nom du répertoire actuel (sans chemin complet) dans un script Bash


Réponses:


1119

Pas besoin de nom de base, et surtout pas besoin d'un sous-shell exécutant pwd (ce qui ajoute une opération fork supplémentaire et coûteuse ); le shell peut le faire en interne en utilisant l' expansion des paramètres :

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.

Notez que si vous appliquez cette technique dans d'autres circonstances (pas PWD, mais une autre variable contenant un nom de répertoire), vous devrez peut-être supprimer les barres obliques de fin. Ce qui suit utilise le support extglob de bash pour fonctionner même avec plusieurs barres obliques de fin:

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"

Alternativement, sans extglob:

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

31
Quelle est la différence entre $ {PWD ## * /} et $ PWD?
Mr_Chimp

24
@Mr_Chimp l'ancien est une opération d'expansion de paramètres qui coupe le reste du répertoire pour ne fournir que le nom de base. J'ai un lien dans ma réponse à la documentation sur l'expansion des paramètres; n'hésitez pas à suivre cela si vous avez des questions.
Charles Duffy

24
@stefgosselin $PWDest le nom d'une variable bash intégrée; tous les noms de variables intégrés sont en majuscules (pour les distinguer des variables locales, qui doivent toujours avoir au moins un caractère en minuscule). result=${PWD#*/}ne pas évaluer à /full/path/to/directory; au lieu de cela, il supprime uniquement le premier élément, ce qui le rend path/to/directory; l'utilisation de deux #caractères rend le motif correspondant gourmand, en faisant correspondre autant de caractères que possible. Lisez la page d'expansion des paramètres, liée dans la réponse, pour en savoir plus.
Charles Duffy

5
Notez que la sortie de pwdn'est pas toujours la même que la valeur de $PWD, en raison des complications résultant de l' cding via des liens symboliques, si bash a l' -o physicaloption définie, et ainsi de suite. Cela était particulièrement désagréable pour la gestion des répertoires montés automatiquement, où l'enregistrement du chemin physique au lieu du chemin logique produirait un chemin qui, s'il était utilisé, permettrait à l'automonteur de démonter spontanément le répertoire utilisé.
Alex North-Keys

3
Agréable! Quelqu'un devrait écrire un livre pour les vieux gars de la coquille Borne comme moi. Peut-être qu'il y en a un là-bas? Il pourrait y avoir un ancien administrateur système entrejambe disant des trucs comme: "à l'époque, nous n'avions que shet cshet si vous vouliez que la touche de retour arrière fonctionne, vous deviez lire toute la sttypage de manuel, et nous l'avons aimé!"
Red Cricket

336

Utilisez le basenameprogramme. Pour votre cas:

% basename "$PWD"
bin

20
N'est-ce pas le but du basenameprogramme? Quel est le problème avec cette réponse en plus de manquer les citations?
Nacht - Reinstate Monica

11
@Nacht basenameest en effet un programme externe qui fait la bonne chose, mais en cours d' exécution tout programme externe pour une bash chose peut faire hors de la boîte en utilisant uniquement la fonctionnalité intégrée est stupide, encourant impact sur les performances ( fork(), execve(), wait(), etc.) pour sans raison.
Charles Duffy

3
... en passant, mes objections à basenameici ne s'appliquent pas dirname, car dirnamea des fonctionnalités qui ${PWD##*/}ne le font pas - transformer une chaîne sans barre oblique ., par exemple. Ainsi, bien que l'utilisation en dirnametant qu'outil externe présente un surcoût de performances, il possède également des fonctionnalités qui aident à compenser ces derniers.
Charles Duffy

4
@LouisMaddox, un peu de kibitzing: le functionmot-clé est incompatible avec POSIX sans ajouter de valeur à la syntaxe standardisée, et le manque de guillemets créerait des bogues dans les noms de répertoires avec des espaces. wdexec() { "./$(basename "$PWD")"; }pourrait être une alternative préférable.
Charles Duffy

28
Bien sûr, l'utilisation basenameest moins "efficace". Mais il est probablement plus efficace en termes de productivité des développeurs, car il est facile à retenir par rapport à la syntaxe bash laide. Donc, si vous vous en souvenez, allez-y. Sinon, ça basenamemarche aussi bien. Quelqu'un a-t-il déjà dû améliorer les "performances" d'un script bash et le rendre plus efficace?
usr

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

La


2
@jocap ... sauf que l'utilisation de l'écho là-bas, sans citer l'argument, est incorrecte . Si le nom du répertoire a plusieurs espaces ou un caractère de tabulation, il sera réduit à un seul espace; s'il a un caractère générique, il sera développé. Une utilisation correcte serait echo "${PWD##*/}".
Charles Duffy

9
@jocap ... aussi, je ne suis pas sûr que "écho" soit en fait une partie légitime de la réponse. La question était de savoir comment obtenir la réponse, pas comment imprimer la réponse; si l'objectif était de l'assigner à une variable, par exemple, ce name=${PWD##*/}serait bien et ce name=$(echo "${PWD##*/}")serait faux (dans un sens d'inefficacité inutile).
Charles Duffy

24

Vous pouvez utiliser une combinaison de pwd et de nom de base. Par exemple

#!/bin/bash

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

echo "$BASENAME"

exit;

18
Je t'en prie, non. Les backticks créent un sous-shell (donc une opération de fork) - et dans ce cas, vous les utilisez deux fois ! [En plus, un problème de style - les noms de variables doivent être en minuscules, sauf si vous les exportez vers l'environnement ou s'il s'agit d'un cas spécial réservé].
Charles Duffy

11
et comme second style, les rétroprojections devraient être remplacées par $ (). toujours des fourches mais plus lisibles et avec moins d'échanges nécessaires.
Jeremy Wall

1
Par convention, les variables d'environnement (PATH, EDITOR, SHELL, ...) et les variables internes du shell (BASH_VERSION, RANDOM, ...) sont entièrement capitalisées. Tous les autres noms de variables doivent être en minuscules. Étant donné que les noms de variables sont sensibles à la casse, cette convention évite de remplacer accidentellement les variables environnementales et internes.
Rany Albeg Wein

2
Dépend de la convention. On pourrait faire valoir que toutes les variables shell sont des variables d'environnement (selon le shell), et donc toutes les variables shell devraient être en majuscules. Quelqu'un d'autre pourrait argumenter en fonction de la portée - les variables globales sont toutes majuscules, tandis que les variables locales sont en minuscules, et ce sont toutes des globales. Un autre pourrait encore soutenir que le fait de rendre les variables en majuscules ajoute une couche supplémentaire de contraste avec les commandes qui jonchent les scripts shell, qui sont également tous des mots courts mais significatifs en minuscules. Je peux ou non / être d'accord avec l'un de ces arguments, mais j'ai vu les trois en usage. :)
dannysauer

17

Que diriez-vous de grep:

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

Je ne le recommanderais pas car il semble obtenir des informations sur les couleurs alors pwd | xargs basenamequ'il ne le fait pas .. probablement pas si important mais l'autre réponse est plus simple et plus cohérente dans tous les environnements
davidhq

8

J'aime la réponse choisie (Charles Duffy), mais faites attention si vous êtes dans un répertoire avec lien symbolique et que vous voulez le nom du répertoire cible. Malheureusement, je ne pense pas que cela puisse être fait dans une expression d'extension de paramètre unique, peut-être que je me trompe. Cela devrait fonctionner:

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

Pour voir cela, une expérience:

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

rapports "bar"

DIRNAME

Pour afficher les principaux répertoires d'un chemin (sans encourir un fork-exec de / usr / bin / dirname):

echo ${target_PWD%/*}

Cela transformera par exemple foo / bar / baz -> foo / bar


5
Malheureusement, readlink -fc'est une extension GNU, et donc pas disponible sur les BSD (y compris OS X).
Xiong Chiamiov

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

Si vous utilisez le shell Bourne ou ${PWD##*/}n'est pas disponible.


4
Pour info, ${PWD##*/}est POSIX sh - tous les / bin / sh modernes (y compris dash, ash, etc.) le supportent; pour frapper Bourne réel sur une boîte récente, vous devez être sur un système Solaris légèrement ancien. Au-delà de cela - echo "$PWD"; omettre les guillemets conduit à des bugs (si le nom du répertoire a des espaces, des caractères génériques, etc.).
Charles Duffy

1
Oui, j'utilisais un ancien système Solaris. J'ai mis à jour la commande pour utiliser des guillemets.
anomalie

7

Ce fil est génial! Voici une autre saveur:

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

5

Étonnamment, personne n'a mentionné cette alternative qui utilise uniquement les commandes bash intégrées:

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

En prime, vous pouvez facilement obtenir le nom du répertoire parent avec:

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

Ceux-ci fonctionneront sur Bash 4.3-alpha ou plus récent.


étonnamment ? je plaisante, mais d'autres sont beaucoup plus courts et plus faciles à retenir
codewandler

C'est assez drôle = P n'avait pas entendu parler de $ IFS (séparateur de champ interne) avant cela. ma bash était trop vieille, mais je peux la tester ici: jdoodle.com/test-bash-shell-script-online
Stan Kurdziel

5

Utilisation:

basename "$PWD"

OU

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

Retournez le séparateur de nom de fichier interne (IFS) dans l'espace.

IFS= 

Il y a un espace après l'IFS.


4
basename $(pwd)

ou

echo "$(basename $(pwd))"

2
Il a besoin de plus de guillemets pour être correct - en l'état, la sortie de pwdest divisée en chaînes et étendue par glob avant d'être transmise à basename.
Charles Duffy

Ainsi, basename "$(pwd)"- bien que ce soit très inefficace par rapport à juste basename "$PWD", ce qui est lui - même inefficace par rapport à l'utilisation d'une expansion de paramètre au lieu d'appeler basenamedu tout.
Charles Duffy

4

Pour les jockeys de découverte comme moi:

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

Modifiez le $PWDpour "$PWD"gérer correctement les noms de répertoire inhabituels.
Charles Duffy

3

je l'utilise habituellement dans les scripts sh

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

vous pouvez l'utiliser pour automatiser les choses ...


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

2

Ci-dessous grep avec regex fonctionne également,

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

3
Cela ne fonctionne pas si le nom du répertoire contient des caractères "-".
daniula

1

Utilisez simplement:

pwd | xargs basename

ou

basename "`pwd`"

2
Il s'agit à tous égards d'une version pire de la réponse donnée précédemment par Arkady ( stackoverflow.com/a/1371267/14122 ): le xargsformultion est inefficace et bogué lorsque les noms de répertoire contiennent des sauts de ligne littéraux ou des guillemets, et la deuxième formulation appelle la pwdcommande dans un sous-shell, plutôt que de récupérer le même résultat via une expansion de variable intégrée.
Charles Duffy

@CharlesDuffy Néanmoins, est-ce une réponse valide et pratique à la question.
bachph


0

Vous pouvez utiliser l'utilitaire de nom de base qui supprime tout préfixe se terminant par / et le suffixe (s'il est présent dans la chaîne) de la chaîne, et imprime le résultat sur la sortie standard.

$basename <path-of-directory>

0

Je préfère fortement utiliser gbasename, qui fait partie de coreutils GNU.


Pour info, il ne vient pas avec ma distribution Ubuntu.
Katastic Voyage

@KatasticVoyage, c'est là sur Ubuntu, ça s'appelle juste basenamelà. Il n'est généralement appelé que gbasenamesur MacOS et d'autres plates-formes qui autrement sont livrées avec un nom de base non GNU.
Charles Duffy

-1

Les commandes suivantes entraîneront l'impression de votre répertoire de travail actuel dans un script bash.

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

2
(1) Je ne sais pas où nous nous assurons que la liste d'arguments est telle qu'elle $1contiendra le répertoire de travail actuel. (2) Le pushdet popdne sert à rien ici parce que tout ce qui se trouve à l'intérieur des backticks est fait dans un sous-shell - donc cela ne peut pas affecter le répertoire du shell parent pour commencer. (3) L'utilisation "$(cd "$1"; pwd)"serait à la fois plus lisible et résistante aux noms de répertoire avec des espaces.
Charles Duffy
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.