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 tr
soluçã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, sed
claro, 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 OUTPUT
variá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 tr
mas 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 dash
qual normalmente aspas seguras são saídas entre aspas simples, como '"'"'
. bash
faria '\''
.
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 $IFS
e $*
.
set -f; IFS=\"\'\`; set -- $var; printf %s "$*"
SAÍDA
"some ""crazy """"""""string ""here
Aí eu só printf
para vocês verem, mas claro, se eu tivesse feito:
var="$*"
... em vez do valor do printf
comando $var
seria o que você vê na saída.
Quando eu set -f
instruo 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 +f
em qualquer linha que eu queira.
A divisão de campo ocorre com base nos caracteres em $IFS
.
Existem dois tipos de $IFS
valores – $IFS
espaço em branco e $IFS
não espaço em branco. $IFS
espaç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, $IFS
matrizes 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 $IFS
campos delimitados gerados pela $var
expansão do. Quando é expandido, seus valores constituintes para os caracteres contidos nele $IFS
sã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 $IFS
quando você o divide. Você poderia alterar$IFS
depois set -- $args
para 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