Quiero obtener el nombre de archivo (sin extensión) y la extensión por separado.
La mejor solución que encontré hasta ahora es:
NAME=`echo "$FILE" | cut -d'.' -f1`
EXTENSION=`echo "$FILE" | cut -d'.' -f2`
Esto está mal porque no funciona si el nombre del archivo contiene varios .
caracteres. Si, digamos, tengo a.b.js
, considerará a
y b.js
, en lugar de a.b
y js
.
Se puede hacer fácilmente en Python con
file, ext = os.path.splitext(path)
pero preferiría no encender un intérprete de Python solo por esto, si es posible.
¿Alguna idea mejor?
extension="{$filename##*.}"
como lo hice por un tiempo! Mover el$
exterior de los rizos: Derecha:extension="${filename##*.}"
os.path.splitext
como el anterior ...Respuestas:
Primero, obtenga el nombre del archivo sin la ruta:
Alternativamente, puede centrarse en el último '/' de la ruta en lugar del '.' que debería funcionar incluso si tiene extensiones de archivo impredecibles:
Es posible que desee consultar la documentación:
fuente
basename
extension=$([[ "$filename" = *.* ]] && echo ".${filename##*.}" || echo '')
. Tenga en cuenta que si una extensión es presente, se le devolverá incluyendo la inicial.
, por ejemplo,.txt
.Para obtener más detalles, consulte la expansión de parámetros de shell en el manual de Bash.
fuente
dinosaurs.in.tar
y gzipped adinosaurs.in.tar.gz
:)x.tar.gz
extensión esgz
y el nombre de archivo esx.tar
eso. No existen las extensiones duales. Estoy bastante seguro de que boost :: filesystem lo maneja de esa manera. (ruta dividida, change_extension ...) y su comportamiento se basa en python si no me equivoco.Por lo general, ya conoce la extensión, por lo que es posible que desee utilizar:
por ejemplo:
y obtenemos
fuente
basename
es bastante revelador, tipo amable señor / señora :).zip
o.ZIP
. ¿Hay alguna manera de hacer algo asíbasename $file {.zip,.ZIP}
?Puede usar la magia de la expansión de parámetros POSIX:
Hay una advertencia de que si su nombre era de la forma
./somefile.tar.gz
a continuación,echo ${FILENAME%%.*}
eliminaría con avidez el partido más largo de la.
y que tendría la cadena vacía.(Puede solucionar eso con una variable temporal:
)
Este sitio explica más.
fuente
cut
no tiene--complement
ysed
no tiene-r
.Eso no parece funcionar si el archivo no tiene extensión o no tiene nombre de archivo. Esto es lo que estoy usando; solo usa builtins y maneja más (pero no todos) nombres de archivos patológicos.
Y aquí hay algunos casos de prueba:
fuente
dir="${fullpath:0:${#fullpath} - ${#filename}}"
que he visto a menudodir="${fullpath%$filename}"
. Es más sencillo de escribir. No estoy seguro de si hay alguna diferencia de velocidad real o problemas.which bash
->/bin/bash
; tal vez es tu distro?Puedes usar
basename
.Ejemplo:
Debe proporcionar el nombre base con la extensión que se eliminará, sin embargo, si siempre está ejecutando
tar
con,-z
entonces sabe que la extensión será.tar.gz
.Esto debería hacer lo que quieras:
fuente
cd $(basename $1 .tar.gz)
funciona para archivos .gz. Pero en cuestión mencionóArchive files have several extensions: tar.gz, tat.xz, tar.bz2
funciona bien, así que puedes usar:
Los comandos, por cierto, funcionan de la siguiente manera.
El comando para
NAME
sustituye un"."
carácter seguido de cualquier número de no"."
caracteres hasta el final de la línea, sin nada (es decir, elimina todo desde el final"."
hasta el final de la línea, inclusive). Esto es básicamente una sustitución no codiciosa que usa trucos regex.El comando para
EXTENSION
sustituye cualquier número de caracteres seguido de un"."
carácter al comienzo de la línea, sin nada (es decir, elimina todo desde el inicio de la línea hasta el punto final, inclusive). Esta es una sustitución codiciosa, que es la acción predeterminada.fuente
sed 's,\.[^\.]*$,,'
para nombre ysed 's,.*\.,., ;t ;g'
para extensión (usa el atípicotest
y losget
comandos, junto con elsubstitute
comando típico ).Mellen escribe en un comentario en una publicación de blog:
Usando Bash, también hay
${file%.*}
que obtener el nombre de archivo sin la extensión y${file##*.}
obtener solo la extensión. Es decir,Salidas:
fuente
No hay necesidad de molestarse con
awk
osed
o inclusoperl
para esta tarea simple. Hay un puro Bash,os.path.splitext()
solución compatible con que solo utiliza expansiones de parámetros.Implementación de referencia
Documentación de
os.path.splitext(path)
:Código de Python:
Implementación de Bash
Honrando los períodos principales
Ignorando los períodos principales
Pruebas
Aquí hay casos de prueba para la implementación de ignorar los períodos iniciales, que deben coincidir con la implementación de referencia de Python en cada entrada.
Resultados de la prueba
Todas las pruebas pasaron.
fuente
text.tar.gz
debe sertext
y la extensión ser.tar.gz
os.path.splitext
Python. Si esa implementación es sensata para posibles aportaciones controvertidas es otro tema."$root"
)? ¿Qué podría pasar si se omitieran? (No pude encontrar ninguna documentación al respecto). ¿Cómo maneja esto los nombres de archivo con*
o?
en ellos?*
y?
no son especiales. Entonces las dos partes de mi pregunta se responden entre sí. ¿Estoy en lo cierto de que esto no está documentado? ¿O se supone que esto debe entenderse por el hecho de que las citas deshabilitan la expansión global en general?root="${path#?}";root="${path::1}${root%.*}"
- luego proceda de la misma manera para extraer la extensión.Puede usar el
cut
comando para eliminar las dos últimas extensiones (la".tar.gz"
parte):Como señaló Clayton Hughes en un comentario, esto no funcionará para el ejemplo real en la pregunta. Entonces, como alternativa, propongo usar
sed
expresiones regulares extendidas, como esta:Funciona eliminando las dos últimas extensiones (alfanuméricas) incondicionalmente.
[Actualizado nuevamente después del comentario de Anders Lindahl]
fuente
$
para verificar que la extensión coincidente esté al final del nombre del archivo. De lo contrario, un nombre de archivo comoi.like.tar.gz.files.tar.bz2
podría producir un resultado inesperado.sed
orden de la cadena. Incluso con$
al final un nombre de archivo comompc-1.0.1.tar.bz2.tar.gz
eliminará ambos.tar.gz
y luego.tar.bz2
.Aquí hay algunas sugerencias alternativas (principalmente en
awk
), incluidos algunos casos de uso avanzados, como extraer números de versión para paquetes de software.Todos los casos de uso utilizan la ruta completa original como entrada, sin depender de resultados intermedios.
fuente
La respuesta aceptada funciona bien en típicos casos , pero no en los bordes de los casos , a saber:
extension=${filename##*.}
devuelve el nombre de archivo de entrada en lugar de una cadena vacía.extension=${filename##*.}
no incluye la inicial.
, contrario a la convención..
no funcionaría para nombres de archivo sin sufijo.filename="${filename%.*}"
será la cadena vacía, si el nombre del archivo de entrada comienza con.
y no contiene más.
caracteres (p. ej..bash_profile
), contrario a la convención.---------
Por lo tanto, la complejidad de una solución robusta que cubre todos los casos extremos requiere una función : consulte su definición a continuación; se puede volver todos los componentes de un camino .
Llamada de ejemplo:
Tenga en cuenta que los argumentos después de la ruta de entrada se eligen libremente, nombres de variables posicionales .
Para omitir variables que no son de interés anteriores a las que sí lo son, especifique
_
(para usar la variable desechable$_
) o''
; por ejemplo, para extraer la raíz y la extensión del nombre de archivo solamente, usesplitPath '/etc/bash.bashrc' _ _ fnameroot extension
.Código de prueba que ejerce la función:
Resultado esperado: tenga en cuenta los casos límite:
.
( no considera el inicio del sufijo)/
(final/
se ignora el ).
se devuelve como la ruta principal).
un token prefijado (solo el último se considera el sufijo):fuente
La solución más pequeña y más simple (en una sola línea) es:
fuente
echo
. En general,echo $(command)
está mejor escrito simplemente acommand
menos que requiera específicamente que el shell realice la tokenización de espacios en blanco y la expansión de comodines en la salida desdecommand
antes de mostrar el resultado. Prueba: ¿cuál es el resultado deecho $(echo '*')
(y si eso es lo que realmente quieres, realmente realmente solo quieresecho *
).echo
comando en absoluto. Lo acabo de usar para demostrar el resultadofoo
que aparece en la tercera línea como resultado de la segunda línea.basename "${file%.*}"
haría lo mismo; está utilizando una sustitución de comando para capturar su salida, solo aecho
esa misma salida de inmediato. (Sin citar, el resultado es nominalmente diferente; pero eso no es relevante, mucho menos una característica, aquí)basename "$file" .txt
evita la complejidad de la sustitución de parámetros.Creo que si solo necesita el nombre del archivo, puede intentar esto:
Y eso es todo = D.
fuente
Puede forzar el corte para mostrar todos los campos y los siguientes que se agregan
-
al número de campo.Entonces, si ARCHIVO es
eth0.pcap.gz
, la EXTENSIÓN serápcap.gz
Usando la misma lógica, también puede buscar el nombre del archivo usando '-' con el corte de la siguiente manera:
Esto funciona incluso para nombres de archivo que no tienen ninguna extensión.
fuente
Reconocimiento de archivos mágicos
Además de la gran cantidad de buenas respuestas sobre esta pregunta de desbordamiento de pila, me gustaría agregar:
Bajo Linux y otros Unixen, hay un comando mágico llamado
file
, que detecta el tipo de archivo al analizar algunos primeros bytes del archivo. Esta es una herramienta muy antigua, utilizada inicialmente para servidores de impresión (si no se creó para ... no estoy seguro de eso).Se pueden encontrar extensiones de estándares en
/etc/mime.types
(en mi escritorio Debian GNU / Linux. Verman file
yman mime.types
. Quizás tenga que instalar lafile
utilidad y losmime-support
paquetes):Podrías crear un golpetazofunción para determinar la extensión correcta. Hay una pequeña muestra (no perfecta):
Esta función podría establecer una variable Bash que se pueda usar más tarde:
(Esto está inspirado en la respuesta correcta de @Petesh):
fuente
Ok por lo que si he entendido bien, el problema aquí es cómo conseguir que el nombre y la extensión completa de un archivo que tiene múltiples extensiones, por ejemplo,
stuff.tar.gz
.Esto funciona para mi:
Esto le dará
stuff
como nombre de archivo y.tar.gz
como extensión. Funciona para cualquier cantidad de extensiones, incluyendo 0. Espero que esto ayude a cualquiera que tenga el mismo problema =)fuente
os.path.splitext
, que es lo que quiere el OP) es('stuff.tar', '.gz')
.Yo uso el siguiente script
fuente
Esto atiende a múltiples puntos y espacios en un nombre de archivo, sin embargo, si no hay extensión, devuelve el nombre de archivo en sí. Aunque es fácil de verificar; solo pruebe que el nombre de archivo y la extensión sean los mismos.
Naturalmente, este método no funciona para archivos .tar.gz. Sin embargo, eso podría manejarse en un proceso de dos pasos. Si la extensión es gz, verifique nuevamente para ver si también hay una extensión tar.
fuente
Cómo extraer el nombre de archivo y la extensión en peces :
Advertencias: se divide en el último punto, que funciona bien para nombres de archivo con puntos, pero no para extensiones con puntos. Ver ejemplo a continuación.
Uso:
Probablemente hay mejores formas de hacer esto. Siéntase libre de editar mi respuesta para mejorarla.
Si hay un conjunto limitado de extensiones con las que se enfrentará y las conoce todas, intente esto:
Esto no tiene la advertencia como primer ejemplo, pero tiene que manejar cada caso, por lo que podría ser más tedioso dependiendo de cuántas extensiones pueda esperar.
fuente
Aquí hay un código con AWK . Se puede hacer de manera más simple. Pero no soy bueno en AWK.
fuente
split()
.awk -F / '{ n=split($2, a, "."); print a[n] }' uses
/ `como delimitador de nivel superior, pero luego divide los segundos campos.
e imprime el último elemento de la nueva matriz.Simplemente use
${parameter%word}
En tu caso:
Si quieres probarlo, todo lo siguiente funciona y simplemente elimina la extensión:
fuente
=
letreros.A partir de la respuesta de Petesh , si solo se necesita el nombre de archivo, tanto la ruta como la extensión se pueden quitar en una sola línea,
fuente
filename="$(basename "${fullname%.*}")"
basename
es opcional, pero especifica la extensión que se va a quitar. La sustitución aún puede ser útil, pero tal vez enbasename
realidad no lo sea, ya que en realidad puede realizar todas estas sustituciones con cascarillas incorporadas.Basado en gran medida en los excelentes bashismos de @ mklement0 y repletos de bashismos útiles y aleatorios, así como otras respuestas a esta / otras preguntas / "ese maldito internet" ... Lo resumí todo en un poco, un poco más comprensible, función reutilizable para mi (o tu)
.bash_profile
que se encarga de lo que (considero) debería ser una versión más robusta dedirname
/basename
/ lo que tienes ..Ejemplos de uso ...
fuente
$IFS
en absoluto (y si lo fuera, podría usarlolocal
para localizar el efecto de configurarlo). - Mejor usarlocal
variables. - Su mensaje de error debe enviarse astderr
, nostdout
(usar1>&2
), y debe devolver un código de salida distinto de cero. - Es mejor cambiar el nombrefullname
abasename
(el primero sugiere una ruta con componentes dir). -name
agrega incondicionalmente un.
(punto), incluso si el original no tenía ninguno. Simplemente puede usar labasename
utilidad, pero tenga en cuenta que ignora una terminación/
.Una respuesta simple:
Para ampliar la respuesta de las variables POSIX , tenga en cuenta que puede hacer patrones más interesantes. Entonces, para el caso detallado aquí, simplemente puede hacer esto:
Eso cortará la última aparición de .tar. <algo> .
En términos más generales, si desea eliminar la última aparición de. <algo> . <algo más> entonces
Debería funcionar bien.
El enlace de la respuesta anterior parece estar muerto. Aquí hay una gran explicación de un montón de manipulación de cadenas que puede hacer directamente en Bash, desde TLDP .
fuente
Si también desea permitir extensiones vacías , esto es lo más corto que se me ocurre:
Primera línea explicada: coincide con PATH.EXT o CUALQUIER COSA y lo reemplaza con EXT. Si CUALQUIER COSA coincide, el grupo ext no se captura.
fuente
Este es el único que funcionó para mí:
Esto también se puede usar en la interpolación de cadenas, pero desafortunadamente debe configurarlo de
base
antemano.fuente
Aquí está el algoritmo que utilicé para encontrar el nombre y la extensión de un archivo cuando escribí un script Bash para hacer que los nombres sean únicos cuando los nombres entran en conflicto con respecto a la carcasa.
La prueba de funcionamiento.
FYI: El programa completo de transliteración y más casos de prueba se pueden encontrar aquí: https://www.dropbox.com/s/4c6m0f2e28a1vxf/avoid-clashes-code.zip?dl=0
fuente
extension=$([[ "$theFileName" == *.* ]] && echo ".${theFileName##*.}" || echo '')
Usando el archivo de ejemplo
/Users/Jonathan/Scripts/bash/MyScript.sh
, este código:resultará en
${ME}
serMyScript
y${MY_EXT}
ser.sh
:Guión:
Algunas pruebas:
fuente
basename
es, quizás, exagerado.De las respuestas anteriores, la línea más corta para imitar a Python
suponiendo que su archivo realmente tenga una extensión, es
fuente
EXT
para que se trate de tortugas hasta el fondo. (Además, debe evitar todo en mayúsculas para sus nombres de variables privadas; están reservados para las variables del sistema.)