Cómo obtener solo el nombre de archivo usando sed

17

¿Cómo puedo obtener solo el nombre de archivo usando sed? tengo esto

out_file=$(echo $in_file|sed "s/\(.*\.\).*/\1mp4/g")

Pero también entiendo el camino /root/video.mp4, y solo quiero video.mp4.

Shixons
fuente

Respuestas:

26

basenamede GNU coreutils puede ayudarlo a hacer este trabajo:

$ basename /root/video.mp4
video.mp4

Si ya conoce la extensión del archivo, puede invocar basenameusando la sintaxis basename NAME [SUFFIX]para eliminarlo:

$ basename /root/video.mp4 .mp4
video

O otra opción sería cortar todo después del último punto usando sed:

$ basename /root/video.old.mp4 | sed 's/\.[^.]*$//'
video.old
uloBasEI
fuente
3
El uso sed 's/\.[^.]*$//'que usted tiene, se producirá un error de (oculto) .filenamey .y ..directorios
Peter.O
9

La solución más fácil es eliminar todo hasta la última aparición de /:

echo /root/video.mp4 | sed 's/.*\///'

margarita
fuente
5

Utilice cualquiera de las siguientes formas:

out_file="${in_file##*/}"

out_file="$(basename $in_file)"

out_file="$(echo $in_file | sed 's=.*/==')"

out_file="$(echo $in_file | awk -F"/" '{ print $NF }')"

PD. Obtiene la misma cadena porque en su declaración \(.*\.\)coincide con la cadena desde el principio hasta el punto ( /root/video.) y luego agrega manualmente lo .mp4que es lo mismo que en su cadena original. Deberías usar s=.*\([^/]*\)=\1=en su lugar.

Actualización: (El primero está arreglado ahora)

Para obtener el único nombre de archivo sin extensión, puede:

out_file="$(echo $in_file | sed 's=.*/==;s/\.[^.]*$/.new_ext/')"

out_file="$(echo $in_file | sed 's=\([^/]*\)\.[^./]*$=\1.new_ext=')"

out_file="$(echo $in_file | awk -F"/" '{ gsub (/\.[^/.]*$/,".new_ext",$NF);print $NF }'
prisa
fuente
Pero con cualquiera de esos métodos obtengo el nombre de archivo con el formato y necesito obtener solo el nombre de archivo y poner un nuevo formato manualmente.
Shixons
Ah, eso tiene sentido. He actualizado mi respuesta.
prisa el
@rush: Habrá casos extremos, por ejemplo, para un archivo llamado my.file.tar.gz.
donothings exitosamente
@donothings exitosamente faltaba un símbolo de punto en el último sedy awk. Fijo. Gracias.
prisa el
4

Uno de los fundamentos del uso de expresiones regulares es que los patrones son codiciosos por naturaleza al especificar el comodín. Si bien la respuesta propuesta por @uloBasEI es ciertamente una respuesta funcional, también requiere el uso del comando basename. La pregunta original de @Shixons solicita una solución utilizando solo sed.

Antes de continuar, siempre es útil saber qué versión de sed es el objetivo. Asumo BSD (como se envía con OSX).

En primer lugar, el patrón propuesto en la pregunta original no funciona porque captura todo desde el comienzo de la cadena de entrada hasta el último punto, incluido el mismo. Sin anclajes, esta búsqueda se tragará todo de izquierda a derecha. El patrón coincidente "/ 1", por lo tanto, es todo hasta e incluyendo el último punto. Incluso un nombre de archivo con múltiples puntos se tragará entero. No es el resultado deseado en absoluto.

El primer paso es establecer una estrategia para identificar patrones. Aquí, le gustaría deshacerse de todo a la izquierda del nombre del archivo (trataremos la extensión más adelante):

out_file="$(echo $in_file | sed 's/^\(\/.*\/\)*.*/\1/')"

La búsqueda coincide desde el principio de la cadena. Coincide con un patrón de "/.*" cero o más veces y elimina todo después. Imprimimos los patrones coincidentes con "\ 1". No estamos buscando a nivel mundial; estamos buscando desde el principio de la cadena especificando el ancla ^.

Obtenemos una mejor claridad al habilitar la opción "-E" para no tener que escapar de los paréntesis:

out_file="$(echo $in_file | sed -E 's/^(\/.*\/)*.*/\1/')"

Entonces ahora tenemos la parte de la izquierda. Agreguemos la parte de la derecha. Tenga en cuenta que debemos mantener la parte izquierda como patrón porque así es como podemos especificar que aparezca cero o más veces. Todo lo que hacemos ahora es agregar un patrón para la parte de la derecha:

out_file="$(echo $in_file | sed -E 's/^(\/.*\/)*(.*)/\2/')"

Solo imprimimos la segunda coincidencia, descartando todo menos el nombre del archivo. Pero aún necesitamos eliminar la extensión del nombre de archivo.

out_file="$(echo $in_file | sed -E 's/^(\/.*\/)*(.*)\..*$/\2/')"

El "$" al final es opcional.

Finalmente, para agregar la nueva extensión, simplemente revisa así:

out_file="$(echo $in_file | sed -E 's/^(\/.*\/)*(.*)\..*$/\2.mp4/')"

Una optimización adicional es hacer que la primera barra diagonal sea opcional para manejar rutas relativas:

out_file="$(echo $in_file | sed -E 's/^([\/]?.*\/)*(.*)\..*$/\2.mp4/')"

Encontré esta pregunta siendo flojo mientras buscaba un patrón sed para reemplazar el nombre base . Estoy trabajando en un sistema despojado que no tiene ese comando instalado.

markeissler
fuente