Maneira mais curta de remover caracteres em uma variável

Maneira mais curta de remover caracteres em uma variável

Existem muitas maneiras de remover caracteres em uma variável.

O caminho mais curto que descobri até agora étr:

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

Existe uma maneira mais rápida?

E isso é seguro para citações como 'e ele mesmo?"`

Responder1

Vamos ver. O mais curto que posso sugerir é um ajuste na sua trsolução:

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

Outras alternativas incluem a já mencionada substituição de variável que pode ser mais curta do que a mostrada até agora:

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

E, sedclaro, embora isso seja mais longo em termos de personagens:

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

Não tenho certeza se você quer dizer o mais curto em duração ou em termos de tempo gasto. Em termos de comprimento, esses dois são os mais curtos possíveis (ou o que consigo de qualquer maneira) quando se trata de remover esses caracteres específicos. Então, qual é o mais rápido? Testei definindo a OUTPUTvariável como você tinha no seu exemplo, mas repeti várias dezenas de vezes:

$ 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 você pode ver, o tré claramente o mais rápido, seguido de perto pelo sed. Além disso, parece que using echoé um pouco mais rápido que using <<<:

$ 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 a diferença é pequena, executei os testes acima 10 vezes para cada um dos dois e descobri que o mais rápido é de fato aquele com o qual você começou:

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

Porém, isso muda quando você leva em conta a sobrecarga de atribuição a uma variável, aqui, o uso tré um pouco mais lento que a simples substituição:

$ 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

Portanto, para concluir, quando você quiser simplesmente visualizar os resultados, use trmas se quiser reatribuir a uma variável, usar os recursos de manipulação de string do shell é mais rápido, pois evitam a sobrecarga de executar um subshell separado.

Responder2

Você poderia usarsubstituição de variável:

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

Use essa sintaxe: ${parameter//pattern/string}para substituir todas as ocorrências do padrão pela string.

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

Responder3

No bash ou zsh é:

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

Observe que isso ${VAR//PATTERN/}remove todas as instâncias do padrão. Para maiores informaçõesexpansão do parâmetro bash

Esta solução deve ser mais rápida para strings curtas porque não envolve a execução de nenhum programa externo. No entanto, para strings muito longas, o oposto é verdadeiro – é melhor usar uma ferramenta dedicada para operações de texto, por exemplo:

$ 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

Responder4

Se, por acaso, você estiver apenas tentando lidar com aspas para reutilização no shell, poderá fazer issosemremovê-los, e é muito simples também:

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

Esse shell de função cita qualquer array de argumentos que você entrega e incrementa sua saída por argumento iterável.

Aqui está com alguns argumentos:

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

SAÍDA

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

Essa saída é a partir da dashqual normalmente aspas seguras são saídas entre aspas simples, como '"'"'. bashfaria '\''.

A substituição de uma seleção de bytes únicos, sem espaços em branco e não nulos por outro byte único provavelmente pode ser feita mais rapidamente em qualquer shell POSIX com $IFSe $*.

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

SAÍDA

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

Aí eu só printfpara vocês verem, mas claro, se eu tivesse feito:

var="$*"

... em vez do valor do printfcomando $varseria o que você vê na saída.

Quando eu set -finstruo o shellnãopara glob - caso a string contenha caracteres que possam ser interpretados como padrões glob. Eu faço isso porque o analisador de shells expande os padrões globdepoisele executa a divisão de campos em variáveis. globbing pode ser reativado como set +f. Em geral - em scripts - acho útil definir meu estrondo como:

#!/usr/bin/sh -f

E então paraativar explicitamente o globbingcom set +fem qualquer linha que eu queira.

A divisão de campo ocorre com base nos caracteres em $IFS.

Existem dois tipos de $IFSvalores – $IFSespaço em branco e $IFSnão espaço em branco. $IFSespaço em branco(espaço, tabulação, nova linha)campos delimitados são especificados para serem eliminados porseqüênciapara um único campo(ou nenhum, se não precederem outra coisa)- então...

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

Mas todos os outros são especificados para serem avaliados em um único campopor ocorrência- eles não estão truncados.

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

Todosexpansões de variáveis ​​são, por padrão, $IFSmatrizes de dados delimitadas - elas são divididas em campos separados de acordo com $IFS. Quando você "-quote one, você substitui essa propriedade da matriz e a avalia como uma única string.

Então quando eu faço...

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

Estou configurando a matriz de argumentos do shell para os muitos $IFScampos delimitados gerados pela $varexpansão do. Quando é expandido, seus valores constituintes para os caracteres contidos nele $IFSsãoperdido- eles são apenas separadores de campos agora - eles são \0NUL.

"$*"- como outras expansões variáveis ​​​​entre aspas duplas - também substitui as qualidades de divisão de campo de $IFS. Mas,além disso, ele substitui o primeiro byte em$IFS para cada campo delimitadoem "$@". Então porque "foi oprimeirovalor em$IFS todos os delimitadores subsequentes se "tornam "$*".E também "não precisa estar presente $IFSquando você o divide. Você poderia alterar$IFS depois set -- $argspara outro valor inteiramente e seunovoo primeiro byte apareceria para os delimitadores de campo em "$*". Além do mais, você pode remover totalmente todos os vestígios deles, como:

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

SAÍDA

some crazy string here

informação relacionada