La documentación se puede encontrar en el manual de Bash . Busque ${parameter%word}y ${parameter%%word}rastree la sección correspondiente de la porción.
Terminé usando este porque era el más flexible y había un par de cosas similares que también quería hacer, lo que hizo muy bien.
Lawrence Johnston
Esta es probablemente la más flexible de todas las respuestas publicadas, pero creo que las respuestas que sugieren los comandos basename y dirname también merecen cierta atención. Pueden ser el truco si no necesita ninguna otra combinación de patrones sofisticados.
mgadda
77
¿Cómo se llama esto $ {x% .bar}? Me gustaría saber más al respecto.
Albahaca
14
@Basil: Expansión de parámetros. En una consola, escriba "man bash" y luego escriba "/ expansion de parámetros"
Zan Lynx
1
Supongo que la explicación de 'man bash' tiene sentido si ya sabes lo que hace o si lo probaste por ti mismo de la manera difícil. Es casi tan malo como la referencia de git. Simplemente lo buscaría en Google.
Probablemente la más simple de todas las soluciones ofrecidas actualmente ... aunque usaría $ (...) en lugar de backticks.
Michael Johnson
66
Más simple pero agrega una dependencia (no una enorme o extraña, lo admito). También necesita saber el sufijo.
Vinko Vrsalovic
Y se puede usar para eliminar cualquier cosa del final, básicamente solo elimina una cadena del final.
Smar
2
El problema es el golpe del tiempo. Acabo de buscar la pregunta para esta discusión después de ver bash tomar casi 5 minutos para procesar 800 archivos, usando basename. Usando el método anterior de expresiones regulares, el tiempo se redujo a aproximadamente 7 segundos. Aunque esta respuesta es más fácil de realizar para el programador, el golpe de tiempo es demasiado. ¡Imagina una carpeta con un par de miles de archivos en ella! Tengo algunas de esas carpetas.
xizdaqrian
@xizdaqrian Esto es absolutamente falso. Este es un programa simple, que no debería tomar medio segundo para regresar. Acabo de ejecutar time find / home / me / dev -name "* .py" .py -exec basename {} \; y eliminó la extensión y el directorio de 1500 archivos en 1 segundo en total.
Laszlo Treszkai
40
Golpe puro, hecho en dos operaciones separadas:
Eliminar la ruta de una cadena de ruta:
path=/foo/bar/bim/baz/file.gif
file=${path##*/} #$file is now 'file.gif'
Usando basename utilicé lo siguiente para lograr esto:
for file in*;do
ext=${file##*.}
fname=`basename $file $ext`# Do things with $fnamedone;
Esto no requiere un conocimiento a priori de la extensión del archivo y funciona incluso cuando tiene un nombre de archivo que tiene puntos en su nombre de archivo (delante de su extensión); sin embargo, requiere el programa basename, pero esto es parte de los coreutils de GNU, por lo que debe enviarse con cualquier distribución.
Sería útil arreglar la cita aquí; tal vez ejecute esto a través de shellcheck.net con mystring=$1el valor constante actual en lugar del valor constante (que suprimirá varias advertencias, asegurándose de no contener espacios / caracteres globales / etc.) y aborde los problemas. encuentra?
Charles Duffy
Bueno, hice algunos cambios apropiados para apoyar las comillas en $ mystring. Gosh esto fue hace mucho tiempo escribí esto :)
Jerub
Sería una mejora adicional citar los resultados: de echo "basename: $(basename "$mystring")"esa manera, si mystring='/foo/*'no se *reemplaza con una lista de archivos en el directorio actual después debasename finalizar.
Charles Duffy
6
Usar basenamesupone que sabes cuál es la extensión del archivo, ¿no?
Y creo que las diversas sugerencias de expresiones regulares no hacen frente a un nombre de archivo que contiene más de un "."
Lo siguiente parece hacer frente a los puntos dobles. Ah, y los nombres de archivo que contienen un "/" en sí (solo por diversión)
Parafraseando a Pascal, "Lo siento, este guión es tan largo. No tuve tiempo para acortarlo"
Esto incluso puede hacerse seguro para el nombre de archivo separando la salida con bytes NUL usando la -zopción, por ejemplo, para estos archivos que contienen espacios en blanco, líneas nuevas y caracteres globales (entre comillas ls):
$ ls has*'has'$'\n''newline.bar''has space.bar''has*.bar'
Si no puede usar basename como se sugiere en otras publicaciones, siempre puede usar sed. Aquí hay un ejemplo (feo). No es el mejor, pero funciona extrayendo la cadena deseada y reemplazando la entrada con la cadena deseada.
echo '/foo/fizzbuzz.bar'| sed 's|.*\/\([^\.]*\)\(\..*\)$|\1|g'
Aunque esta es la respuesta a la pregunta original, este comando es útil cuando tengo líneas de rutas en un archivo para extraer nombres base para imprimirlos en la pantalla.
Sangcheol Choi
2
Tenga cuidado con la solución perl sugerida: elimina cualquier cosa después del primer punto.
$ echo some.file.with.dots | perl -pe 's/\..*$//;s{^.*/}{}'
some
El nombre base hace eso, elimina el camino. También eliminará el sufijo si se proporciona y si coincide con el sufijo del archivo, pero necesitaría saber el sufijo para darle el comando. De lo contrario, puede usar mv y descubrir cuál debería ser el nuevo nombre de otra manera.
${parameter%word}
y${parameter%%word}
en la sección de coincidencia de la parte posterior.Respuestas:
Aquí se explica cómo hacerlo con los operadores # y% en Bash.
${x%.bar}
También podría ser${x%.*}
eliminar todo después de un punto o${x%%.*}
eliminar todo después del primer punto.Ejemplo:
La documentación se puede encontrar en el manual de Bash . Busque
${parameter%word}
y${parameter%%word}
rastree la sección correspondiente de la porción.fuente
mira el comando basename:
fuente
Golpe puro, hecho en dos operaciones separadas:
Eliminar la ruta de una cadena de ruta:
Elimine la extensión de una cadena de ruta:
fuente
Usando basename utilicé lo siguiente para lograr esto:
Esto no requiere un conocimiento a priori de la extensión del archivo y funciona incluso cuando tiene un nombre de archivo que tiene puntos en su nombre de archivo (delante de su extensión); sin embargo, requiere el programa
basename
, pero esto es parte de los coreutils de GNU, por lo que debe enviarse con cualquier distribución.fuente
fname=`basename $file .$ext`
Pura manera bash:
Esta funcionalidad se explica en man bash en "Expansión de parámetros". Abundan las formas no bash: awk, perl, sed, etc.
EDITAR: funciona con puntos en sufijos de archivo y no necesita conocer el sufijo (extensión), pero no funciona con puntos en el nombre mismo.
fuente
Las funciones basename y dirname son lo que buscas:
Tiene salida:
fuente
mystring=$1
el valor constante actual en lugar del valor constante (que suprimirá varias advertencias, asegurándose de no contener espacios / caracteres globales / etc.) y aborde los problemas. encuentra?echo "basename: $(basename "$mystring")"
esa manera, simystring='/foo/*'
no se*
reemplaza con una lista de archivos en el directorio actual después debasename
finalizar.Usar
basename
supone que sabes cuál es la extensión del archivo, ¿no?Y creo que las diversas sugerencias de expresiones regulares no hacen frente a un nombre de archivo que contiene más de un "."
Lo siguiente parece hacer frente a los puntos dobles. Ah, y los nombres de archivo que contienen un "/" en sí (solo por diversión)
Parafraseando a Pascal, "Lo siento, este guión es tan largo. No tuve tiempo para acortarlo"
fuente
Además de la sintaxis conforme POSIX utilizada en esta respuesta ,
como en
GNU
basename
admite otra sintaxis:con el mismo resultado La diferencia y la ventaja es que eso
-s
implica-a
, que admite múltiples argumentos:Esto incluso puede hacerse seguro para el nombre de archivo separando la salida con bytes NUL usando la
-z
opción, por ejemplo, para estos archivos que contienen espacios en blanco, líneas nuevas y caracteres globales (entre comillasls
):Lectura en una matriz:
readarray -d
requiere Bash 4.4 o posterior. Para versiones anteriores, tenemos que hacer un bucle:fuente
fuente
Si no puede usar basename como se sugiere en otras publicaciones, siempre puede usar sed. Aquí hay un ejemplo (feo). No es el mejor, pero funciona extrayendo la cadena deseada y reemplazando la entrada con la cadena deseada.
Que te dará la salida
fuente
Tenga cuidado con la solución perl sugerida: elimina cualquier cosa después del primer punto.
Si quieres hacerlo con perl, esto funciona:
Pero si está utilizando Bash, las soluciones con
y=${x%.*}
(obasename "$x" .ext
si conoce la extensión) son mucho más simples.fuente
El nombre base hace eso, elimina el camino. También eliminará el sufijo si se proporciona y si coincide con el sufijo del archivo, pero necesitaría saber el sufijo para darle el comando. De lo contrario, puede usar mv y descubrir cuál debería ser el nuevo nombre de otra manera.
fuente
Combinando la respuesta mejor calificada con la segunda respuesta mejor calificada para obtener el nombre de archivo sin la ruta completa:
fuente