Il existe de nombreuses façons de remplacer des caractères dans une variable.
Le moyen le plus court que j'ai découvert est tr
jusqu'à présent:
OUTPUT=a\'b\"c\`d_123and_a_lot_more
OUTPUT=$(echo "$OUTPUT"|tr -d "'\`\"")
echo $OUTPUT
Y at-il un moyen plus rapide? Et est-ce sûr pour les citations comme '
, "
et `lui-même?
Voyons voir. Le plus court que je puisse trouver est un Tweak de votre solution tr
:
OUTPUT="$(tr -d "\"\`'" <<<$OUTPUT)"
D'autres alternatives incluent la substitution de variables déjà mentionnée qui peut être plus courte que celle illustrée jusqu'à présent:
OUTPUT="${OUTPUT//[\'\"\`]}"
Et sed
bien sûr, bien que ce soit plus long en termes de caractères:
OUTPUT="$(sed s/[\'\"\`]//g <<<$OUTPUT)"
Je ne sais pas si vous voulez dire la durée la plus courte ou en termes de temps pris. En termes de longueur, ces deux sont aussi courts que possible (ou comme je peux l'obtenir de toute façon) quand il s'agit de supprimer ces caractères spécifiques. Alors, quel est le plus rapide? J'ai testé en définissant la variable OUTPUT
sur ce que vous aviez dans votre exemple, mais répété plusieurs dizaines de fois:
$ 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
Comme vous pouvez le voir, le tr
est clairement le plus rapide, suivi de près par sed
. De plus, il semble que l'utilisation de echo
soit en fait légèrement plus rapide que celle de <<<
:
$ 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
Étant donné que la différence est minuscule, j'ai exécuté les tests ci-dessus 10 fois pour chacun des deux et il s'avère que le plus rapide est en effet celui avec lequel vous avez dû commencer:
echo $OUTPUT | tr -d "\"\`'"
Cependant, cela change lorsque vous prenez en compte la surcharge d'affectation à une variable, ici, l'utilisation de tr
est légèrement plus lente que le remplacement simple:
$ 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
Donc, en conclusion, lorsque vous souhaitez simplement afficher les résultats, utilisez tr
mais si vous souhaitez réaffecter à une variable, l'utilisation des fonctionnalités de manipulation de chaîne du Shell est plus rapide car elles évitent la surcharge d'exécution d'un sous-shell séparé.
Vous pouvez utiliser substitution de variable :
$ OUTPUT=a\'b\"c\`d
$ echo "$OUTPUT"
a'b"c`d
Utilisez cette syntaxe: ${parameter//pattern/string}
pour remplacer toutes les occurrences du motif par la chaîne.
$ echo "${OUTPUT//\'/x}"
axb"c`d
$ echo "${OUTPUT//\"/x}"
a'bxc`d
$ echo "${OUTPUT//\`/x}"
a'b"cxd
$ echo "${OUTPUT//[\'\"\`]/x}"
axbxcxd
En bash ou zsh c'est:
OUTPUT="${OUTPUT//[\`\"\']/}"
Notez que ${VAR//PATTERN/}
supprime toutes les instances du modèle. Pour plus d'informations expansion des paramètres bash
Cette solution devrait être plus rapide pour les chaînes courtes car elle n'implique pas l'exécution de programmes externes. Cependant, pour les très longues chaînes, l'inverse est vrai - il est préférable d'utiliser un outil dédié pour les opérations de texte, par exemple:
$ 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
Si, par hasard, vous essayez simplement de gérer les devis pour la réutilisation du Shell, alors vous pouvez le faire sans les supprimer, et c'est aussi simple que cela:
aq() { sh -c 'for a do
alias "$((i=$i+1))=$a"
done; alias' -- "$@"
}
Cette fonction Shell cite tout tableau d'arguments que vous lui donnez et incrémente sa sortie par argument itérable.
Le voici avec quelques arguments:
aq \
"here's an
ugly one" \
"this one is \$PATHpretty bad, too" \
'this one```****```; totally sucks'
1='here'"'"'s an
ugly one'
2='this one is $PATHpretty bad, too'
3='this one```****```; totally sucks'
Cette sortie provient de dash
, qui cite généralement les sorties entre guillemets simples comme '"'"'
. bash
ferait '\''
.
Le remplacement d'une sélection d'octets simples, non blancs et non nuls par un autre octet unique peut probablement se faire le plus rapidement dans n'importe quel shell POSIX avec $IFS
Et $*
.
set -f; IFS=\"\'\`; set -- $var; printf %s "$*"
"some ""crazy """"""""string ""here
Là je l'ai juste printf
pour que vous puissiez le voir, mais bien sûr, si je l'avais fait:
var="$*"
... plutôt que la valeur de la commande printf
$var
serait ce que vous voyez dans la sortie là-bas.
Quand je set -f
J'instruis le Shell pas à glob - au cas où la chaîne contient des caractères qui pourraient être interprétés comme des motifs glob. Je le fais parce que l'analyseur de shells étend les modèles globaux après il effectue la division du champ sur les variables. la globalisation peut être réactivée comme set +f
. En général - dans les scripts - je trouve utile de définir mon bang comme:
#!/usr/bin/sh -f
Et puis activer explicitement la globalisation avec set +f
Sur la ligne que je pourrais vouloir.
La division des champs se produit en fonction des caractères dans $IFS
.
Il existe deux types de valeurs $IFS
- $IFS
Espaces blancs et $IFS
Non blancs. $IFS
Espaces blancs (espace, tabulation, nouvelle ligne) les champs délimités sont spécifiés pour être éliminés par séquence vers un seul champ (ou aucun du tout si ils ne précèdent pas autre chose) - donc ...
IFS=\ ; var=' '; printf '<%s>' $var
<>
Mais tous les autres sont spécifiés pour être évalués sur un seul champ par occurrence - ils ne sont pas tronqués.
IFS=/; var='/////'; printf '<%s>' $var
<><><><><>
Tous les extensions de variables sont, par défaut, des tableaux de données délimités par $IFS
- ils se répartissent en champs séparés selon $IFS
. Lorsque vous "
- en citez une, vous remplacez cette propriété de tableau et l'évaluez comme une chaîne unique.
Alors quand je fais ...
IFS=\"\'\`; set -- $var
Je mets le tableau d'arguments du Shell sur les nombreux champs délimités par $IFS
Générés par l'expansion de $var
. Lorsqu'il est développé, ses valeurs constitutives pour les caractères contenus dans $IFS
Sont perdues - ce ne sont plus que des séparateurs de champs - ce sont \0NUL
.
"$*"
- comme les autres extensions de variable entre guillemets doubles - remplace également les qualités de division de champ de $IFS
. Mais, en plus, il remplace le premier octet dans $IFS
pour chaque champ délimité dans "$@"
. Donc parce que "
Était la valeur première dans $IFS
tous les délimiteurs suivants deviennent "
Dans "$*"
. = Et le "
N'a pas besoin d'être dans $IFS
Lorsque vous le divisez non plus. Vous pouvez modifier $IFS
aprèsset -- $args
En une autre valeur et son nouvea premier octet apparaîtra alors pour les délimiteurs de champ dans "$*"
. De plus, vous pouvez en supprimer toutes les traces comme:
set -- $var; IFS=; printf %s "$*"
some crazy string here