¿Cómo puedo eliminar la extensión de un nombre de archivo en un script de shell?


147

¿Qué hay de malo con el siguiente código?

name='$filename | cut -f1 -d'.''

Como es, obtengo la cadena literal $filename | cut -f1 -d'.', pero si elimino las comillas no obtengo nada. Mientras tanto, escribiendo

"test.exe" | cut -f1 -d'.'

en un shell me da la salida que quiero test,. Ya sé que $filenamese le ha asignado el valor correcto. Lo que quiero hacer es asignar a una variable el nombre del archivo sin la extensión.


66
basename $filename .exeHaría lo mismo. Eso suponiendo que siempre sepas qué extensión deseas eliminar.
MPE

66
@mpe, quieres decir basename "$filename" .exe. De lo contrario, los nombres de archivo con espacios serían malas noticias.
Charles Duffy

Respuestas:


114

Debe usar la sintaxis de sustitución de comandos $(command)cuando desee ejecutar un comando en script / command.

Entonces tu línea sería

name=$(echo "$filename" | cut -f 1 -d '.')

Explicación del código:

  1. echoobtener el valor de la variable $filenamey enviarlo a la salida estándar
  2. Luego tomamos la salida y la canalizamos al cutcomando
  3. El cututilizará el. como delimitador (también conocido como separador) para cortar la cadena en segmentos y -fseleccionamos qué segmento queremos tener en la salida
  4. Luego, la $()sustitución del comando obtendrá la salida y devolverá su valor
  5. El valor devuelto se asignará a la variable denominada name

Tenga en cuenta que esto proporciona la parte de la variable hasta el primer período .:

$ filename=hello.world
$ echo "$filename" | cut -f 1 -d '.'
hello
$ filename=hello.hello.hello
$ echo "$filename" | cut -f 1 -d '.'
hello
$ filename=hello
$ echo "$filename" | cut -f 1 -d '.'
hello

Gracias. También noté que necesito usar el comando echo. name =echo $filename | cut -f1 -d'.'
mimicocotopus

17
Los backticks son obsoletos por POSIX, $()se prefiere.
jordanm

3
Bifurcar y canalizar para obtener algunos caracteres es la peor solución imaginable.
Jens

39
El problema con esta respuesta es que supone que la cadena de entrada tiene SOLO un punto ... @chepner a continuación tiene una solución mucho mejor ... name = $ {filename%. *}
Scott Stensland

44
Esta respuesta es característica de los principiantes y no debe difundirse. Use el mecanismo incorporado como se describe en la respuesta de
chepner

260

También puede usar la expansión de parámetros:

$ filename=foo.txt
$ echo "${filename%.*}"
foo

66
Aquí la explicación del comando: gnu.org/software/bash/manual/html_node/…
Carlos Robles

1
Y aquí estaba a punto de usar echo -n "This.File.Has.Periods.In.It.txt" | awk -F. '{$NF=""; print $0}' | tr ' ' '.' | rev | cut -c 2- | rev. Gracias.
user208145

1
¿Funciona esto con archivos con múltiples extensiones como image.png.gz?
Hawker65

11
%.*solo eliminará la última extensión; si desea eliminar todas las extensiones, use %%.*.
chepner

1
Esto parece funcionar mucho mejor que la respuesta aceptada. Solo elimina la última extensión si el archivo tiene más de un punto, mientras que cortar elimina todo después del primer punto.
Dale Anderson el


20

Si su nombre de archivo contiene un punto (que no sea el de la extensión), use esto:

echo $filename | rev | cut -f 2- -d '.' | rev

1
Olvidé la revolución media, pero una vez que la vi, ¡fue genial!
supremo Pooba

Es aún mejor con una -sopción dada cut, de modo que devuelva una cadena vacía siempre que el nombre del archivo no contenga punto.
Hibou57

1
Esta debería ser la respuesta aceptada en mi opinión, ya que funciona en la ruta con puntos en ellos, con archivos ocultos que comienzan con un punto, o incluso con archivos con múltiples extensiones.
Tim Krief

20
file1=/tmp/main.one.two.sh
t=$(basename "$file1")                        # output is main.one.two.sh
name=$(echo "$file1" | sed -e 's/\.[^.]*$//') # output is /tmp/main.one.two
name=$(echo "$t" | sed -e 's/\.[^.]*$//')     # output is main.one.two

usa lo que quieras. Aquí supongo que el último .(punto) seguido del texto es una extensión.


6
#!/bin/bash
file=/tmp/foo.bar.gz
echo $file ${file%.*}

salidas:

/tmp/foo.bar.gz /tmp/foo.bar

Tenga en cuenta que solo se elimina la última extensión.


3

Mi recomendación es usar basename.
Es por defecto en Ubuntu, código visualmente simple y se ocupa de la mayoría de los casos.

Aquí hay algunos sub-casos para tratar con espacios y múltiples puntos / sub-extensión:

pathfile="../space fld/space -file.tar.gz"
echo ${pathfile//+(*\/|.*)}

Por lo general, se deshace de la extensión desde el principio ., pero falla en nuestro ..camino

echo **"$(basename "${pathfile%.*}")"**  
space -file.tar     # I believe we needed exatly that

Aquí hay una nota importante:

Usé comillas dobles dentro de comillas dobles para tratar con espacios. La cotización simple no pasará debido a los mensajes de texto de $. Bash es inusual y lee "segunda" primera "cita" debido a la expansión.

Sin embargo, aún debes pensar en .hidden_files

hidden="~/.bashrc"
echo "$(basename "${hidden%.*}")"  # will produce "~" !!!  

no el "" resultado esperado. Para que esto suceda, use $HOMEo /home/user_path/
porque de nuevo bash es "inusual" y no expanda "~" (busque bash BashPitfalls)

hidden2="$HOME/.bashrc" ;  echo '$(basename "${pathfile%.*}")'

1
#!/bin/bash
filename=program.c
name=$(basename "$filename" .c)
echo "$name"

salidas:

program

¿Cómo es esto diferente de la respuesta dada por Steven Penny hace 3 años?
gniourf_gniourf

1

Como señaló Hawker65 en el comentario de la respuesta de chepner, la solución más votada no se ocupa de múltiples extensiones (como filename.tar.gz), ni de puntos en el resto de la ruta (como this.path / with .dots / in.path.name). Una posible solución es:

a=this.path/with.dots/in.path.name/filename.tar.gz
echo $(dirname $a)/$(basename $a | cut -d. -f1)

Éste elimina "tar.gz" seleccionando caracteres antes de la primera instancia de un punto en el nombre del archivo sin contar la ruta. Probablemente uno no quiera quitar las extensiones de esa manera.
Frotz

0

Dos problemas con su código:

  1. Usó un '(marca) en lugar de un' (marca de retroceso) para rodear los comandos que generan la cadena que desea almacenar en la variable.
  2. No "echo" la variable "$ filename" a la tubería en el comando "cortar".

Cambiaría su código a "name =` echo $ filename | cut -f 1 -d '.' `", como se muestra a continuación (nuevamente, observe los ticks posteriores que rodean la definición de la variable de nombre):

$> filename=foo.txt
$> echo $filename
foo.txt
$> name=`echo $filename | cut -f1 -d'.'`
$> echo $name
foo
$> 
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.