Obtener el nombre del directorio actual (sin ruta completa) en un script Bash


Respuestas:


1118

No necesita basename, y especialmente no necesita una subshell que ejecute pwd (que agrega una operación de horquilla adicional y costosa ); el shell puede hacer esto internamente usando la expansión de parámetros :

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.

Tenga en cuenta que si está aplicando esta técnica en otras circunstancias (no PWD, pero alguna otra variable que contenga un nombre de directorio), es posible que deba recortar las barras diagonales finales. A continuación se utiliza el soporte extglob de bash para trabajar incluso con múltiples barras diagonales:

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"

Alternativamente, sin extglob:

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

31
¿Cuál es la diferencia entre $ {PWD ## * /} y $ PWD?
Mr_Chimp

24
@Mr_Chimp the first es una operación de expansión de parámetros que recorta el resto del directorio para proporcionar solo el nombre base. Tengo un enlace en mi respuesta a la documentación sobre la expansión de parámetros; siéntase libre de seguir eso si tiene alguna pregunta.
Charles Duffy

24
@stefgosselin $PWDes el nombre de una variable bash incorporada; Todos los nombres de variables incorporados están en mayúsculas (para distinguirlos de las variables locales, que siempre deben tener al menos un carácter en minúscula). result=${PWD#*/}Cómo no valorar a /full/path/to/directory; en su lugar, despoja solo el primer elemento, haciéndolo path/to/directory; El uso de dos #caracteres hace que el patrón coincida con la codicia, haciendo coincidir tantos caracteres como sea posible. Lea la página de expansión de parámetros, vinculada en la respuesta, para más información.
Charles Duffy

55
Tenga en cuenta que el resultado de pwdno siempre es el mismo que el valor de $PWD, debido a las complicaciones que surgen de cdla conexión a través de enlaces simbólicos, si bash tiene la -o physicalopción establecida, etc. Esto solía volverse especialmente desagradable en el manejo de directorios montados automáticamente, donde grabar la ruta física en lugar de la lógica produciría una ruta que, si se usa, permitiría al automontador desmontar espontáneamente el directorio que estaba usando.
Alex North-Keys

3
¡Agradable! Alguien debería escribir un libro para los viejos tipos de conchas de Borne como yo. Tal vez hay uno por ahí? Podría tener un crotchity viejos Administración de Sistemas diciendo cosas como, "en mis tiempos sólo shy cshy si queríamos la tecla de retroceso para trabajar había que leer toda la sttypágina hombre, y nos gustó!"
Red Cricket

336

Usa el basenameprograma. Para su caso:

% basename "$PWD"
bin

20
¿No es este el propósito del basenameprograma? ¿Qué hay de malo en esta respuesta además de faltar las comillas?
Nacht - Restablece a Mónica el

11
@Nacht basenamees de hecho un programa externo que hace lo correcto, pero el funcionamiento de cualquier programa externo para una fiesta que se puede hacer fuera de la caja utilizando solamente funcionalidad integrada es tonto, incurriendo impacto en el rendimiento ( fork(), execve(), wait(), etc.) para Sin razón.
Charles Duffy

3
... como un aparte, mis objeciones basenameaquí no se aplican dirname, porque dirnametiene una funcionalidad que ${PWD##*/}no lo hace, transformando una cadena sin barras ., por ejemplo. Por lo tanto, si bien el uso dirnamecomo herramienta externa tiene una sobrecarga de rendimiento, también tiene una funcionalidad que ayuda a compensarlo.
Charles Duffy

44
@LouisMaddox, un poco de kibitzing: la functionpalabra clave es incompatible con POSIX sin agregar ningún valor sobre la sintaxis estandarizada, y la falta de comillas crearía errores en los nombres de directorio con espacios. wdexec() { "./$(basename "$PWD")"; }podría ser una alternativa preferible.
Charles Duffy

28
Claro que usar basenamees menos "eficiente". Pero probablemente sea más eficiente en términos de productividad del desarrollador porque es fácil de recordar en comparación con la sintaxis de bash feo. Entonces, si lo recuerdas, hazlo. De lo contrario, basenamefunciona igual de bien. ¿Alguien ha tenido que mejorar el "rendimiento" de un script bash y hacerlo más eficiente?
usr

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

​​​​​


2
@jocap ... excepto que el uso de echo allí, sin citar el argumento, es incorrecto . Si el nombre del directorio tiene múltiples espacios, o un carácter de tabulación, se contraerá en un solo espacio; Si tiene un carácter comodín, se expandirá. El uso correcto sería echo "${PWD##*/}".
Charles Duffy

99
@jocap ... además, no estoy seguro de que "echo" sea de hecho una parte legítima de la respuesta. La pregunta era cómo obtener la respuesta, no cómo imprimir la respuesta; si el objetivo fuera asignarlo a una variable, por ejemplo, name=${PWD##*/}sería correcto y name=$(echo "${PWD##*/}")sería incorrecto (en un sentido de ineficiencia innecesaria).
Charles Duffy el

24

Puede usar una combinación de pwd y basename. P.ej

#!/bin/bash

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

echo "$BASENAME"

exit;

18
Por favor no. Los backticks crean una subshell (por lo tanto, una operación de horquilla), y en este caso, ¡los estás usando dos veces ! [Como una objeción adicional, aunque estilística, los nombres de las variables deben estar en minúsculas a menos que los exporte al entorno o sean un caso especial y reservado].
Charles Duffy, el

11
y, como segundo estilo, los backs de quibble deben ser reemplazados por $ (). todavía se bifurca pero es más legible y se necesita menos excaping.
Jeremy Wall

1
Por convención, las variables de entorno (PATH, EDITOR, SHELL, ...) y las variables internas de shell (BASH_VERSION, RANDOM, ...) están totalmente en mayúsculas. Todos los demás nombres de variables deben estar en minúsculas. Dado que los nombres de las variables distinguen entre mayúsculas y minúsculas, esta convención evita anular accidentalmente las variables ambientales e internas.
Rany Albeg Wein

2
Depende de la convención. Se podría argumentar que todas las variables de shell son variables de entorno (dependiendo del shell) y, por lo tanto, todas las variables de shell deben estar en mayúsculas. Alguien más podría argumentar en función del alcance: las variables globales son mayúsculas, mientras que las variables locales son minúsculas, y todas son globales. Otro podría argumentar que hacer que las variables sean mayúsculas agrega una capa adicional de contraste con los comandos que ensucian los scripts de shell, que también son todas palabras cortas pero significativas en minúsculas. Puedo / no puedo / estoy de acuerdo / con cualquiera de esos argumentos, pero he visto los tres en uso. :)
dannysauer

17

¿Qué tal grep:

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

no lo recomendaría porque parece obtener información sobre el color, mientras pwd | xargs basenameque no ... probablemente no es tan importante, pero la otra respuesta es más simple y más consistente en todos los entornos
davidhq

8

Me gusta la respuesta seleccionada (Charles Duffy), pero tenga cuidado si está en un directorio con enlace simbólico y desea el nombre del directorio de destino. Desafortunadamente, no creo que se pueda hacer en una expresión de expansión de un solo parámetro, tal vez me equivoque. Esto debería funcionar:

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

Para ver esto, un experimento:

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

informa "bar"

DIRNAME

Para mostrar los directorios iniciales de una ruta (sin incurrir en un fork-exec de / usr / bin / dirname):

echo ${target_PWD%/*}

Esto, por ejemplo, transformará foo / bar / baz -> foo / bar


55
Desafortunadamente, readlink -fes una extensión de GNU y, por lo tanto, no está disponible en los BSD (incluido OS X).
Xiong Chiamiov

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

Si está utilizando Bourne Shell o ${PWD##*/}no está disponible.


44
Para su información, ${PWD##*/}es POSIX sh: todo moderno / bin / sh (incluido el tablero, ceniza, etc.) lo admite; para llegar a Bourne real en una caja reciente, necesitarías estar en un sistema Solaris ligeramente antiguo. Más allá de eso - echo "$PWD"; omitir las comillas conduce a errores (si el nombre del directorio tiene espacios, caracteres comodín, etc.).
Charles Duffy

1
Sí, estaba usando un viejo sistema Solaris. He actualizado el comando para usar comillas.
anomal

7

Este hilo es genial! Aquí hay un sabor más:

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

5

Sorprendentemente, nadie mencionó esta alternativa que usa solo comandos bash incorporados:

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

Como una ventaja adicional , puede obtener fácilmente el nombre del directorio principal con:

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

Estos funcionarán en Bash 4.3-alpha o más reciente.


sorprendentemente? Sólo bromeando, pero otros son mucho más corto y más fácil de recordar
codewandler

Esto es bastante divertido = P No había oído hablar de $ IFS (separador de campo interno) antes de esto. mi golpe era demasiado viejo, pero puedo probarlo aquí: jdoodle.com/test-bash-shell-script-online
Stan Kurdziel

5

Utilizar:

basename "$PWD"

O

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

Convierta el separador de nombre de archivo interno (IFS) de nuevo al espacio.

IFS= 

Hay un espacio después del IFS.


4
basename $(pwd)

o

echo "$(basename $(pwd))"

2
Necesita más comillas para ser correcto: tal como está, la salida de pwdse divide en cadenas y se expande globalmente antes de pasar a basename.
Charles Duffy

Por lo tanto, basename "$(pwd)"aunque eso es muy ineficiente en comparación con solo basename "$PWD", lo que en sí mismo es ineficiente en comparación con el uso de una expansión de parámetros en lugar de llamar basenameen absoluto.
Charles Duffy

4

Para los jinetes de búsqueda como yo:

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

Cambie $PWDa "$PWD"para manejar correctamente los nombres de directorio inusuales.
Charles Duffy

3

generalmente uso esto en scripts sh

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

puedes usar esto para automatizar cosas ...


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

2

Debajo de grep con regex también funciona,

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

3
No funciona si el nombre del directorio contiene caracteres "-".
daniula

1

Solo usa:

pwd | xargs basename

o

basename "`pwd`"

2
Esta es, en todos los aspectos, una versión peor de la respuesta dada anteriormente por Arkady ( stackoverflow.com/a/1371267/14122 ): la xargsformulación es ineficaz y tiene errores cuando los nombres de directorio contienen nuevas líneas literales o caracteres de comillas, y la segunda formulación llama al pwdcomando en una subshell, en lugar de recuperar el mismo resultado a través de una expansión variable incorporada.
Charles Duffy

@CharlesDuffy Sin embargo, es una respuesta válida y práctica a la pregunta.
bachph


0

Puede usar la utilidad basename que elimina cualquier prefijo que termine en / y el sufijo (si está presente en una cadena) de la cadena e imprime el resultado en la salida estándar.

$basename <path-of-directory>

0

Prefiero usar gbasename, que es parte de GNU coreutils.


Para su información, no viene con mi distribución de Ubuntu.
Katastic Voyage

@KatasticVoyage, está allí en Ubuntu, solo se llama basenameallí. Por lo general, solo se llama gbasenameen MacOS y otras plataformas que de lo contrario se envían con un nombre base no GNU.
Charles Duffy

-1

Los siguientes comandos darán lugar a la impresión de su directorio de trabajo actual en un script bash.

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

2
(1) No estoy seguro de dónde estamos asegurando que la lista de argumentos sea tal que $1contendrá el directorio de trabajo actual. (2) El pushdy popdno sirve para nada aquí porque cualquier cosa dentro de los backticks se realiza en una subshell, por lo que no puede afectar el directorio del shell principal para empezar. (3) El uso "$(cd "$1"; pwd)"sería más legible y resistente frente a los nombres de directorio con espacios en blanco.
Charles Duffy
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.