¿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.

mimicocotopus
fuente
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
Rohan
fuente
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
chepner
fuente
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
117

Si conoce la extensión, puede usar basename

$ basename /home/jsmith/base.wiki .wiki
base
Steven Penny
fuente
¿Cómo puedo eliminar solo .wikiy terminar con /home/jsmith/base?
Gabriel Staples
Actualización: respuesta: consulte stackoverflow.com/a/32584935/4561887 . ¡Funciona perfectamente!
Gabriel Staples
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
manish_s
fuente
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.

Raghwendra
fuente
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.

C. Paul Bond
fuente
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%.*}")'
Alexey K.
fuente
1
#!/bin/bash
filename=program.c
name=$(basename "$filename" .c)
echo "$name"

salidas:

program
Boo Boo
fuente
¿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)
MadMage
fuente
É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
$> 
FanDeLaU
fuente