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


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.


241

Puedes hacer así:

#!/bin/bash

v="some string.rtf"

v2=${v::-4}

echo "$v --> $v2"

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%.*}.


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.


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}


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

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


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


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


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

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.