La forma más corta de eliminar caracteres en una variable

La forma más corta de eliminar caracteres en una variable

Hay muchas formas de eliminar caracteres de una variable.

La forma más corta que descubrí hasta ahora estr:

OUTPUT=a\'b\"c\`d_123and_a_lot_more
OUTPUT=$(echo "$OUTPUT"|tr -d "'\`\"")
echo $OUTPUT

¿Hay una manera mas rápida?

¿Y estas citas son seguras para citas como 'y "para `sí mismo?

Respuesta1

Vamos a ver. Lo más corto que se me ocurre es un ajuste de su trsolución:

OUTPUT="$(tr -d "\"\`'" <<<$OUTPUT)"

Otras alternativas incluyen la sustitución de variables ya mencionada, que puede ser más corta de lo que se muestra hasta ahora:

OUTPUT="${OUTPUT//[\'\"\`]}"

Y sedpor supuesto, aunque esto es más largo en términos de personajes:

OUTPUT="$(sed s/[\'\"\`]//g <<<$OUTPUT)"

No estoy seguro de si te refieres a la duración más corta o al tiempo necesario. En términos de longitud, estos dos son lo más cortos posible (o lo que puedo conseguir de todos modos) cuando se trata de eliminar esos caracteres específicos. Entonces, ¿cuál es más rápido? Probé configurando la OUTPUTvariable según lo que tenía en su ejemplo, pero lo repetí varias docenas de veces:

$ echo ${#OUTPUT} 
4900

$ time tr -d "\"\`'" <<<$OUTPUT
real    0m0.002s
user    0m0.004s
sys     0m0.000s
$ time sed s/[\'\"\`]//g <<<$OUTPUT
real    0m0.005s
user    0m0.000s
sys     0m0.000s
$ time echo ${OUTPUT//[\'\"\`]}
real    0m0.027s
user    0m0.028s
sys     0m0.000s

Como puede ver, tres claramente el más rápido, seguido de cerca por sed. Además, parece que usar echoes en realidad un poco más rápido que usar <<<:

$ for i in {1..10}; do 
    ( time echo $OUTPUT | tr -d "\"\`'" > /dev/null ) 2>&1
done | grep -oP 'real.*m\K[\d.]+' | awk '{k+=$1;} END{print k/NR}'; 
0.0025
$ for i in {1..10}; do 
    ( time tr -d "\"\`'" <<<$OUTPUT > /dev/null ) 2>&1 
  done | grep -oP 'real.*m\K[\d.]+' | awk '{k+=$1;} END{print k/NR}'; 
0.0029

Como la diferencia es pequeña, realicé las pruebas anteriores 10 veces para cada uno de los dos y resulta que la más rápida es con la que tenías al principio:

echo $OUTPUT | tr -d "\"\`'" 

Sin embargo, esto cambia cuando se tiene en cuenta la sobrecarga de asignar una variable; aquí, el uso tres ligeramente más lento que el simple reemplazo:

$ for i in {1..10}; do
    ( time OUTPUT=${OUTPUT//[\'\"\`]} ) 2>&1
  done | grep -oP 'real.*m\K[\d.]+' | awk '{k+=$1;} END{print k/NR}'; 
0.0032

$ for i in {1..10}; do
    ( time OUTPUT=$(echo $OUTPUT | tr -d "\"\`'")) 2>&1
  done | grep -oP 'real.*m\K[\d.]+' | awk '{k+=$1;} END{print k/NR}'; 
0.0044

Entonces, en conclusión, cuando simplemente desee ver los resultados, use trpero si desea reasignarlos a una variable, usar las funciones de manipulación de cadenas del shell es más rápido ya que evitan la sobrecarga de ejecutar un subshell separado.

Respuesta2

Podrías usarsustitución de variables:

$ OUTPUT=a\'b\"c\`d
$ echo "$OUTPUT"
a'b"c`d

Utilice esa sintaxis: ${parameter//pattern/string}para reemplazar todas las apariciones del patrón con la cadena.

$ echo "${OUTPUT//\'/x}"
axb"c`d
$ echo "${OUTPUT//\"/x}"
a'bxc`d
$ echo "${OUTPUT//\`/x}"
a'b"cxd
$ echo "${OUTPUT//[\'\"\`]/x}"
axbxcxd

Respuesta3

En bash o zsh es:

OUTPUT="${OUTPUT//[\`\"\']/}"

Tenga en cuenta que ${VAR//PATTERN/}elimina todas las instancias del patrón. Para más informaciónexpansión de parámetros de bash

Esta solución debería ser más rápida para cadenas cortas porque no implica ejecutar ningún programa externo. Sin embargo, para cadenas muy largas ocurre lo contrario: es mejor utilizar una herramienta dedicada para operaciones de texto, por ejemplo:

$ OUTPUT="$(cat /usr/src/linux/.config)"

$ time (echo $OUTPUT | OUTPUT="${OUTPUT//set/abc}")
real    0m1.766s
user    0m1.681s
sys     0m0.002s

$ time (echo $OUTPUT | sed s/set/abc/g >/dev/null)
real    0m0.094s
user    0m0.078s
sys     0m0.006s

Respuesta4

Si, por casualidad, solo está intentando manejar comillas para reutilizarlas en el shell, entonces puede hacer estosineliminarlos, y también es muy sencillo:

aq() { sh -c 'for a do
       alias "$((i=$i+1))=$a"
       done; alias' -- "$@"
}

Ese shell de función cita cualquier matriz de argumentos que le entregue e incrementa su salida por argumento iterable.

Aquí está con algunos argumentos:

aq \
"here's an
ugly one" \
"this one is \$PATHpretty bad, too" \
'this one```****```; totally sucks'

PRODUCCIÓN

1='here'"'"'s an
ugly one'
2='this one is $PATHpretty bad, too'
3='this one```****```; totally sucks'

Esa salida es de dashla que normalmente se obtienen salidas entre comillas simples como '"'"'. bashharía '\''.

Reemplazar una selección de bytes únicos, sin espacios en blanco y no nulos con otro byte único probablemente se pueda hacer más rápido en cualquier shell POSIX con $IFSy $*.

set -f; IFS=\"\'\`; set -- $var; printf %s "$*"

PRODUCCIÓN

"some ""crazy """"""""string ""here

Ahí printflo dejo para que lo veáis, pero claro, si lo hubiera hecho:

var="$*"

... en lugar de que el valor del printfcomando $varsería lo que ve en el resultado allí.

Cuando set -fle doy instrucciones al caparazónnoa global: en caso de que la cadena contenga caracteres que puedan interpretarse como patrones globales. Hago esto porque el analizador de shells expande los patrones globales.despuésrealiza división de campos en variables. El globbing se puede volver a habilitar como set +f. En general, en los scripts, me resulta útil configurar mi bang como:

#!/usr/bin/sh -f

Y luego ahabilitar explícitamente el globbingcon set +fcualquier línea que pueda querer.

La división de campos se produce según los caracteres de $IFS.

Hay dos tipos de $IFSvalores: $IFSespacios en blanco y $IFSsin espacios en blanco. $IFSespacio en blanco(espacio, tabulación, nueva línea)Los campos delimitados se especifican para eludir porsecuenciaa un solo campo(o ninguno en absoluto si no preceden a otra cosa)- entonces...

IFS=\ ; var='      '; printf '<%s>' $var
<>

Pero todos los demás están especificados para evaluar en un solo campo.por ocurrencia- no están truncados.

IFS=/; var='/////'; printf '<%s>' $var
<><><><><>

TodoLas expansiones de variables son, de forma predeterminada, $IFSmatrices de datos delimitadas: se dividen en campos separados según $IFS. Cuando "cita uno, anula esa propiedad de matriz y la evalúa como una sola cadena.

Entonces cuando lo hago...

IFS=\"\'\`; set -- $var

Estoy configurando la matriz de argumentos del shell para los muchos $IFScampos delimitados generados por $varla expansión. Cuando se expande, sus valores constituyentes para los caracteres contenidos en $IFSsonperdido- ahora son solo separadores de campo - lo son \0NUL.

"$*"- al igual que otras expansiones variables entre comillas dobles - también anula las cualidades de división de campos de $IFS. Pero,además, sustituye el primer byte en$IFS para cada campo delimitadoen "$@". Entonces porque "fue elprimerovalor en$IFS todos los delimitadores posteriores se convierten "en "$*".Y "tampoco es necesario que esté incluido $IFScuando lo divides. podrías alterar$IFS después set -- $argsa otro valor completamente y sunuevoEl primer byte aparecería para los delimitadores de campo en "$*". Es más, puedes eliminar todos los rastros de ellos por completo como:

set -- $var; IFS=; printf %s "$*"

PRODUCCIÓN

some crazy string here

información relacionada