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 tr
solució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 sed
por 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 OUTPUT
variable 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, tr
es claramente el más rápido, seguido de cerca por sed
. Además, parece que usar echo
es 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 tr
es 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 tr
pero 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 dash
la que normalmente se obtienen salidas entre comillas simples como '"'"'
. bash
harí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 $IFS
y $*
.
set -f; IFS=\"\'\`; set -- $var; printf %s "$*"
PRODUCCIÓN
"some ""crazy """"""""string ""here
Ahí printf
lo dejo para que lo veáis, pero claro, si lo hubiera hecho:
var="$*"
... en lugar de que el valor del printf
comando $var
sería lo que ve en el resultado allí.
Cuando set -f
le 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 +f
cualquier línea que pueda querer.
La división de campos se produce según los caracteres de $IFS
.
Hay dos tipos de $IFS
valores: $IFS
espacios en blanco y $IFS
sin espacios en blanco. $IFS
espacio 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, $IFS
matrices 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 $IFS
campos delimitados generados por $var
la expansión. Cuando se expande, sus valores constituyentes para los caracteres contenidos en $IFS
sonperdido- 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 $IFS
cuando lo divides. podrías alterar$IFS
después set -- $args
a 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