Eliminar el último carácter de una cadena usando la manipulación de cadena en el script de shell

187

Me gustaría eliminar el último carácter de una cadena, probé este pequeño script:

#! /bin/sh 

t="lkj"
t=${t:-2}
echo $t

pero imprime "lkj", ¿qué estoy haciendo mal?

usuario3581976
fuente

Respuestas:

115

En un shell POSIX, la sintaxis ${t:-2}significa algo diferente: se expande al valor de tsi testá establecido y no es nulo, y de lo contrario al valor 2. Para recortar un solo carácter por expansión de parámetros, la sintaxis que probablemente desee es${t%?}

Tenga en cuenta que en ksh93, basho zsh, ${t:(-2)}o ${t: -2}(tenga en cuenta el espacio) son legales como una expansión de subcadena, pero probablemente no sean lo que desea, ya que devuelven la subcadena que comienza en una posición a 2 caracteres del final (es decir, elimina el primer carácter ide la cadena). cadena ijk).

Consulte la sección de Expansión de parámetros de Shell del Manual de referencia de Bash para obtener más información:

conductor de acero
fuente
44
¿Te gustaría explicar cuál es la magia detrás de '%'? ?
Afraisse
8
@afraisse ${parameter%word}elimina el menor coincidencia de patrones sufijo word- ver la sección de expansión de parámetros deman bash
steeldriver
3
Esto funciona bien para Bash 4.1.2: $ {t%?} Para personas atrapadas con CentOS / RHEL 6.x
Joey T
185

Con bash4.2 y superior, puedes hacer:

${var::-1}

Ejemplo:

$ a=123
$ echo "${a::-1}"
12

Tenga en cuenta que para versiones anteriores bash(por ejemplo, bash 3.2.5en OS X), debe dejar espacios entre y después de los dos puntos:

${var: : -1}
Cuonglm
fuente
13
Esto funciona para la bashversión 4.2-alpha y superior, lástima que la versión a la que tengo acceso sea anterior. : - /
hjk
2
@iamaziz: desde el registro de cambios de bash, la longitud negativa ${var:offset:lenght}se agregó solo en bash 4.2. Quizás OSX agregue su propio parche para bash.
Cuonglm
1
@cuonglm tampoco funciona: /
iamaziz
1
No funciona en mac.
Shinzou
1
MACsters, mira hacia abajo a la respuesta de Russ
P i
67

para eliminar los últimos ncaracteres de una línea que no utiliza sedOR awk:

> echo lkj | rev | cut -c (n+1)- | rev

así, por ejemplo, puede eliminar el último carácter one characterusando esto:

> echo lkj | rev | cut -c 2- | rev

> lk

de la página de revmanual:

DESCRIPCIÓN
La utilidad rev copia los archivos especificados en la salida estándar, invirtiendo el orden de los caracteres en cada línea. Si no se especifican archivos, se lee la entrada estándar.

ACTUALIZAR:

Si no conoce la longitud de la cadena, intente:

$ x="lkj"
$ echo "${x%?}"
lk
Networker
fuente
62

Usar sed debería ser tan rápido como

sed 's/.$//'

Tu único eco es entonces echo ljk | sed 's/.$//'.
Con esto, la cadena de 1 línea podría ser de cualquier tamaño.

1111161171159459134
fuente
10
Tenga en cuenta que, en el caso general, no elimina el último carácter de la cadena , sino el último carácter de cada línea de la cadena .
Stéphane Chazelas 01 de
44

Algunas opciones dependiendo del shell:

  • POSIX: t=${t%?}
  • Bourne: t=`expr " $t" : ' \(.*\).'`
  • zsh / yash: t=${t[1,-2]}
  • bash / zsh: t=${t:0:-1}
  • ksh93 / bash / zsh / mksh: t=${t:0:${#t}-1}
  • ksh93 / bash / zsh / mksh: t=${t/%?}
  • ksh93: t=${t/~(E).$/}
  • es: @ {t=$1} ~~ $t *?

Tenga en cuenta que si bien se supone que todos eliminan el último carácter , encontrará que algunas implementaciones (aquellas que no admiten caracteres de varios bytes) eliminan el último byte (por lo que probablemente corrompería el último carácter si fuera de varios bytes) )

La exprvariante supone $tque no termina en más de un personaje de nueva línea. También devolverá un estado de salida distinto de cero si la cadena resultante termina siendo 0( 000o incluso -0con algunas implementaciones). También podría dar resultados inesperados si la cadena contiene caracteres no válidos.

Stéphane Chazelas
fuente
Agradable y completo! Pero ... supongo que todos esos shells son compatibles con POSIX, por lo que todos deberían usar ese para ser el más portátil. ¡El más pequeño recuento de personajes también!
Russ
@Russ, t=${t%?}no es Bourne, pero no es probable que encuentres un shell Bourne hoy en día. ${t%?}funciona en todos los demás sin embargo.
Stéphane Chazelas
¡No se ofrece la opción de concha de pescado! Probablemente más popular en estos días que ksh93 ...
rien333
@ rien333. Esperaría a que la interfaz se estabilice un poco. fishes trabajo en progreso. 2.3.0, que introdujo el stringbuiltin no se lanzó en el momento de las preguntas y respuestas. Con la versión en la que lo estoy probando, necesita string replace -r '(?s).\z' '' -- $t(y esperaría que quisieran cambiar eso, deberían cambiar las banderas que pasan a PCRE) o más complicadas. También trata mal con los personajes de nueva línea, y sé que también están planeando cambiar eso.
Stéphane Chazelas
Votó por la respuesta POSIX. confirmado trabajando en Bash 3.2.57 (1)
Avindra Goolcharan
26

La respuesta más portátil y más corta es casi seguro:

${t%?}

Esto funciona en bash, sh, ash, dash, busybox / ash, zsh, ksh, etc.

Funciona utilizando la expansión de parámetros de shell de la vieja escuela. Específicamente, %especifica que se elimine el sufijo de parámetro de coincidencia más pequeño tque coincida con el patrón global ?(es decir, cualquier carácter).

Consulte "Eliminar el patrón de sufijo más pequeño" aquí para obtener una explicación (mucho) más detallada y más antecedentes. Consulte también los documentos de su shell (por ejemplo:) man bashen "expansión de parámetros".


Como nota al margen, si en su lugar desea eliminar el primer carácter, lo usaría ${t#?}, ya que las #coincidencias desde el frente de la cadena (prefijo) en lugar de la parte posterior (sufijo).

También vale la pena señalar que ambos %y #have %%y ##versiones, que coinciden con la versión más larga del patrón dado en lugar de la más corta. Tanto ${t%%?}y ${t##?}haría lo mismo como su único operador en este caso, sin embargo (así que no añadir el carácter adicional inútil). Esto se debe a que el ?patrón dado solo coincide con un solo carácter. Mezclar *con algunos no comodines y las cosas se vuelven más interesantes con %%y ##.

Comprender las expansiones de parámetros, o al menos conocer su existencia y saber cómo buscarlos, es increíblemente útil para escribir y descifrar scripts de shell de muchos sabores. Las expansiones de parámetros a menudo parecen vudú de caparazón arcano para muchas personas porque ... bueno ... son vudú de caparazón arcano (aunque bastante bien documentado si sabe buscar "expansión de parámetro"). Sin embargo, definitivamente es bueno tenerlo en el cinturón de herramientas cuando estás atrapado en un caparazón.

Russ
fuente
Corto y dulce, ¡y funciona tanto en MacOS como en Linux!
Dbernard
18
t=lkj
echo ${t:0:${#t}-1}

Obtiene una subcadena de 0 a la longitud de la cadena -1. Sin embargo, tenga en cuenta que esta resta es específica de bash y no funcionará en otros shells.

Por ejemplo, dashno puede analizar incluso

echo ${t:0:$(expr ${#t} - 1)}

Por ejemplo, en Ubuntu, /bin/shesdash

Ángel
fuente
15

También puede usar headpara imprimir todo menos el último carácter.

$ s='i am a string'
$ news=$(echo -n $s | head -c -1)
$ echo $news
i am a strin

Pero desafortunadamente algunas versiones de headno incluyen la -opción principal . Este es el caso de lo headque viene con OS X.

Greenbeansugar
fuente
5

Es bastante fácil hacerlo usando expresiones regulares:

n=2
echo "lkj" | sed "s/\(.*\).\{$n\}/\1/"
unxnut
fuente
5

Algunos refinamientos. Para eliminar más de un carácter, puede agregar varios signos de interrogación. Por ejemplo, para eliminar los dos últimos caracteres de la variable:, $SRC_IP_MSGpuede usar:

SRC_IP_MSG=${SRC_IP_MSG%??}
yuliskov
fuente
4

Solo para completar algunos usos posibles de bash puro:

#!/bin/bash

# Testing substring removal
STR="Exemple string with trailing whitespace "
echo "'$STR'"
echo "Removed trailing whitespace: '${STR:0:${#STR}-1}'"
echo "Removed trailing whitespace: '${STR/%\ /}'"

La primera sintaxis toma una subcadena de una cadena, la sintaxis es Para la segunda, observe el signo, que significa 'desde el final de la línea' y la sintaxis es
${STRING:OFFSET:LENGTH}
%
${STRING/PATTERN/SUBSTITUTION}

Y aquí hay dos formas más cortas de lo mencionado anteriormente

echo "Removed trailing whitespace: '${STR::-1}'"
echo "Removed trailing whitespace: '${STR%\ }'"

Aquí observe nuevamente el %signo, que significa 'Eliminar (es decir, reemplazar con' ') el patrón coincidente más corto (aquí representado por el espacio escapado ' \ ' desde el final del PARÁMETRO , aquí llamado STR

CermakM
fuente
1

Como también podemos usar php en la línea de comandos o scripts de shell. A veces es útil para el análisis quirúrgico.

php -r "echo substr('Hello', 0, -1);" 
// Output hell

Con tubería:

echo "hello" | php -r "echo substr(trim(fgets(STDIN)), 0, -1);"
// Output hell
NVRM
fuente
-1

En ksh:

echo ${ORACLE_SID/%?/}
Alex
fuente