¿Cómo eliminar los últimos n caracteres de una cadena en Bash?

202

Tengo una variable varen un script Bash que contiene una cadena, como:

echo $var
"some string.rtf"

Quiero eliminar los últimos 4 caracteres de esta cadena y asignar el resultado a una nueva variable var2, para que

echo $var2
"some string"

¿Cómo puedo hacer esto?

llamar
fuente
1
Duplicado de extracto-substring-in-bash
Håkon Hægland

Respuestas:

16

Primero, debes ser explícito sobre lo que quieres. Si sabe que la cadena termina .rtfy desea eliminarla .rtf, puede usarla var2=${var%.rtf}. Si no sabe cuál es el sufijo pero desea eliminar todo comenzando por el último ., puede usarlo var2=${var%.*}. Si solo desea mantener todo al principio ., puede usarvar2=${var%%.*} . Esas dos últimas opciones tienen el mismo resultado si solo hay un período, pero si puede haber más de uno, debe decidir cuál desea.

Si realmente desea eliminar siempre un número exacto de caracteres, aquí hay algunas opciones.

Especificaste bash específicamente, así que comenzaremos con bash builtins. La que ha trabajado más tiempo es la misma sintaxis de eliminación de sufijos que utilicé anteriormente: para eliminar cuatro caracteres, usevar2=${var%????} . Necesita un signo de interrogación por cada carácter eliminado, por lo que esto se vuelve difícil de manejar para longitudes de subcadena más grandes.

Ligeramente más recientes es subcadena de extracción: var2=${var::${#var}-4}. Aquí puede colocar cualquier número en lugar de 4para eliminar un número diferente de caracteres. (El ${#var}se reemplaza por la longitud de la cadena, por lo que en realidad se trata de mantener los primeros caracteres (longitud - 4)).

Las versiones más nuevas de bash (específicamente 4+, lo que significa que la que viene con MacOS no funcionará) le permiten simplificar eso para simplemente var2=${var::-4}.

Si en realidad no está utilizando bash pero algún otro shell de tipo POSIX, la eliminación del sufijo seguirá funcionando, incluso en el tablero viejo (donde ninguno de los demás lo hará). En zsh, todos funcionan pero tienes que poner un punto 0entre los dos puntos: var2=${var:0:-4}etc. En ksh, necesitas el 0 y también debes usar la expresión explícita de longitud-4:var2=${var:0:${#var}-4} .

Por supuesto, puede usar la sustitución de comandos para hacerlo con la ayuda de un programa de utilidad; hay muchos que funcionarán, pero algo así var2=$(cut -c -4 <<<"$var")es probablemente la opción más corta.

Mark Reed
fuente
241

Puedes hacer así:

#!/bin/bash

v="some string.rtf"

v2=${v::-4}

echo "$v --> $v2"
phe
fuente
9
bash 4+ creo.
Etan Reisner
69
Es bash4+, pero en la versión anterior puedes usar un poco más ${v::${#v}-4}.
chepner
8
No funcionó para mí, bash dice "Mala sustitución"
Ivan Marjanovic
15
por alguna razón, en zsh ${v::-4}croaks "zsh: cierre de llave esperado". Pero la respuesta de @fredtantini a continuación ${v:0:-4}funciona bien.
Pierre D
66
Error con: -2: expresión de subcadena <0
Edward
174

Para eliminar cuatro caracteres del final de la cadena, use ${var%????}.

Para eliminar todo después del .uso final ${var%.*}.

Etan Reisner
fuente
12
Respuesta de shell pura, y funcionará en BASH 3.x que se encuentra en muchos sistemas que se han negado a implementar BASH 4.x debido a problemas de licencia.
David
1
Esto es genial ya que es robusto contra las cadenas más cortas; las variantes de desplazamiento del índice fallan entonces.
Raphael
funciona en bash 3.2.25 - la mejor respuesta posible - justo lo que estaba buscando
capser
Excelente respuesta ¿Qué tal una eliminación en el frente de la cadena?
user2023370
3
@ user2023370 Consultar la documentación debe revelar que hay una sustitución de parámetros correspondiente ${var#????}para eso.
tripleee
48

Lo que funcionó para mí fue:

echo "hello world" | rev | cut -c5- | rev
# hello w

Pero lo usé para recortar líneas en un archivo, por eso se ve incómodo. El uso real fue:

cat somefile | rev | cut -c5- | rev

cutsolo lo lleva al recorte desde alguna posición inicial, lo cual es malo si necesita filas de longitud variable. Entonces, esta solución invierte ( rev) la cadena y ahora nos relacionamos con su posición final, luego usa cutcomo se mencionó, y la invierte (nuevamente rev) a su orden original.

Reut Sharabani
fuente
Esto no es correcto y no es una respuesta a lo que se pregunta. revinvierte líneas, no cadenas. echo $SOME_VAR | rev | ...probablemente no se comportará como cabría esperar.
Mohammad Nasirifar
2
@Mohammad Debido a las citas rotas, esa es una sola línea.
tripleee
38

Uso de reemplazo de expansión / subcadena variable :

$ {var /% Patrón / Reemplazo}

Si el sufijo de var coincide con el Patrón, sustituya el Reemplazo por el Patrón.

Entonces puedes hacer:

~$ echo ${var/%????/}
some string

Alternativamente,

Si siempre tienes las mismas 4 letras

~$ echo ${var/.rtf/}
some string

Si siempre termina en .xyz:

~$ echo ${var%.*}
some string

También puede usar la longitud de la cadena:

~$ len=${#var}
~$ echo ${var::len-4}
some string

o simplemente echo ${var::-4}

fredtantini
fuente
len=${#var}; echo ${var::len-4}podría acortarse a echo ${var:0:-4}:-) EDITAR: o como señaló @iyonizer, solo echo ${var::-4}...
anishsane
26

Podrías usar sed,

sed 's/.\{4\}$//' <<< "$var"

Ejemplo:

$ var="some string.rtf"
$ var1=$(sed 's/.\{4\}$//' <<< "$var")
$ echo $var1
some string
Avinash Raj
fuente
1
¿Y cómo asigno el resultado var2?
becko
esto es bueno porque funciona en una línea como esta ... | sed 's /. \ {4 \} $ //'. Se puede usar sin usar variables
Sam
3

Intenté lo siguiente y funcionó para mí:

#! /bin/bash

var="hello.c"
length=${#var}
endindex=$(expr $length - 4)
echo ${var:0:$endindex}

Salida: hel

Priya Jain
fuente
1

Espero que el siguiente ejemplo ayude,

echo ${name:0:$((${#name}-10))} -> ${name:start:len}

  • En el comando anterior, el nombre es la variable.
  • start es el punto de partida de la cuerda
  • len es la longitud de la cadena que debe eliminarse.

Ejemplo:

    read -p "Enter:" name
    echo ${name:0:$((${#name}-10))}

Salida:

    Enter:Siddharth Murugan
    Siddhar

Nota: Bash 4.2 agregó soporte para subcadena negativa

Siddharth Murugan
fuente
Esto es mucho más detallado que una sustitución de patrón simple compatible con Bourne como en la respuesta de Etan Reisner.
tripleee
0

Esto funcionó para mí calculando el tamaño de la cadena.
Es fácil que necesite hacer eco del valor que necesita devolver y luego almacenarlo como se muestra a continuación

removechars(){
        var="some string.rtf"
        size=${#var}
        echo ${var:0:size-4}  
    }
    removechars
    var2=$?

alguna cuerda

Saurabh
fuente
0

En este caso, podría usar basename suponiendo que tiene el mismo sufijo en los archivos que desea eliminar.

Ejemplo:

basename -s .rtf "some string.rtf"

Esto devolverá "alguna cadena"

Si no conoce el sufijo y desea que elimine todo después e incluyendo el último punto:

f=file.whateverthisis
basename "${f%.*}"

salidas "archivo"

% significa cortar,. es lo que estás cortando, * es comodín

Greta
fuente