Es gibt viele Möglichkeiten, Zeichen in einer Variablen zu entfernen.
Der kürzeste Weg, den ich bisher gefunden habe, isttr
:
OUTPUT=a\'b\"c\`d_123and_a_lot_more
OUTPUT=$(echo "$OUTPUT"|tr -d "'\`\"")
echo $OUTPUT
Gibt es einen schnelleren Weg?
Und ist dies für Zitate wie und '
sich selbst zitatsicher ?"
`
Antwort1
Mal sehen. Das kürzeste, was mir einfällt, ist eine Abwandlung Ihrer tr
Lösung:
OUTPUT="$(tr -d "\"\`'" <<<$OUTPUT)"
Zu den weiteren Alternativen gehört die bereits erwähnte Variablensubstitution, die jedoch kürzer sein kann als bisher gezeigt:
OUTPUT="${OUTPUT//[\'\"\`]}"
Und sed
natürlich ist dies hinsichtlich der Zeichenanzahl länger:
OUTPUT="$(sed s/[\'\"\`]//g <<<$OUTPUT)"
Ich bin mir nicht sicher, ob Sie die kürzeste in Bezug auf die Länge oder die benötigte Zeit meinen. In Bezug auf die Länge sind diese beiden so kurz wie es nur geht (oder wie ich es jedenfalls hinbekomme), wenn es darum geht, diese bestimmten Zeichen zu entfernen. Also, welches ist am schnellsten? Ich habe es getestet, indem ich die OUTPUT
Variable auf das gesetzt habe, was Sie in Ihrem Beispiel hatten, aber mehrere Dutzend Mal wiederholt habe:
$ 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
Wie Sie sehen, tr
ist eindeutig am schnellsten, dicht gefolgt von sed
. Außerdem scheint die Verwendung von echo
tatsächlich etwas schneller zu sein als die Verwendung von <<<
:
$ 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
Da der Unterschied minimal ist, habe ich die obigen Tests für beide jeweils zehnmal ausgeführt. Dabei stellte sich heraus, dass der schnellste Test tatsächlich derjenige ist, mit dem Sie von Anfang an gearbeitet haben:
echo $OUTPUT | tr -d "\"\`'"
Dies ändert sich jedoch, wenn Sie den Mehraufwand bei der Zuweisung zu einer Variablen berücksichtigen. Hier tr
ist die Verwendung etwas langsamer als das einfache Ersetzen:
$ 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
Zusammenfassend lässt sich also sagen, dass Sie verwenden, wenn Sie einfach nur die Ergebnisse anzeigen möchten. tr
Wenn Sie jedoch einer Variablen neu zuweisen möchten, ist die Verwendung der Zeichenfolgenbearbeitungsfunktionen der Shell schneller, da Sie den Aufwand für die Ausführung einer separaten Subshell vermeiden.
Antwort2
Du könntest benutzenVariablensubstitution:
$ OUTPUT=a\'b\"c\`d
$ echo "$OUTPUT"
a'b"c`d
Verwenden Sie diese Syntax: ${parameter//pattern/string}
um alle Vorkommen des Musters durch die Zeichenfolge zu ersetzen.
$ echo "${OUTPUT//\'/x}"
axb"c`d
$ echo "${OUTPUT//\"/x}"
a'bxc`d
$ echo "${OUTPUT//\`/x}"
a'b"cxd
$ echo "${OUTPUT//[\'\"\`]/x}"
axbxcxd
Antwort3
In Bash oder Zsh lautet es:
OUTPUT="${OUTPUT//[\`\"\']/}"
Beachten Sie, dass ${VAR//PATTERN/}
alle Instanzen des Musters entfernt werden. Weitere InformationenBash-Parametererweiterung
Diese Lösung sollte für kurze Zeichenfolgen am schnellsten sein, da keine externen Programme ausgeführt werden müssen. Für sehr lange Zeichenfolgen gilt jedoch das Gegenteil. Dann ist es besser, ein spezielles Tool für Textoperationen zu verwenden, zum Beispiel:
$ 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
Antwort4
Wenn Sie nur Anführungszeichen für die Wiederverwendung in der Shell behandeln möchten, können Sie Folgendes tun:ohnesie zu entfernen, und es ist auch kinderleicht:
aq() { sh -c 'for a do
alias "$((i=$i+1))=$a"
done; alias' -- "$@"
}
Diese Funktionsshell zitiert jedes Argument-Array, das Sie ihr übergeben, und erhöht ihre Ausgabe pro iterierbarem Argument.
Hier ist es mit einigen Argumenten:
aq \
"here's an
ugly one" \
"this one is \$PATHpretty bad, too" \
'this one```****```; totally sucks'
AUSGABE
1='here'"'"'s an
ugly one'
2='this one is $PATHpretty bad, too'
3='this one```****```; totally sucks'
Diese Ausgabe erfolgt dash
normalerweise in sicheren Anführungszeichen und in einfachen Anführungszeichen, wie es beispielsweise der Fall wäre '"'"'
.bash
'\''
Das Ersetzen einer Auswahl einzelner, nicht aus Leerzeichen bestehender und nicht aus Null bestehender Bytes durch ein anderes einzelnes Byte kann in jeder POSIX-Shell wahrscheinlich am schnellsten mit $IFS
und erfolgen $*
.
set -f; IFS=\"\'\`; set -- $var; printf %s "$*"
AUSGABE
"some ""crazy """"""""string ""here
Hier ist printf
es, damit Sie es sehen können. Aber natürlich hätte ich Folgendes gemacht:
var="$*"
...anstatt des printf
Befehlswerts $var
wäre das, was Sie dort in der Ausgabe sehen.
Wenn ich set -f
die Shell anweisenichtzu glob - falls die Zeichenfolge Zeichen enthält, die als Glob-Muster interpretiert werden könnten. Ich mache das, weil der Shell-Parser Glob-Muster erweitertnaches führt eine Feldaufteilung für Variablen durch. Globbing kann wie folgt wieder aktiviert werden set +f
. Im Allgemeinen finde ich es in Skripten nützlich, meinen Bang wie folgt einzustellen:
#!/usr/bin/sh -f
Und dann zuGlobbing explizit aktivierenmit set +f
welcher Zeile auch immer ich es möchte.
Die Feldaufteilung erfolgt basierend auf den Zeichen in $IFS
.
Es gibt zwei Arten von $IFS
Werten: $IFS
Leerzeichen und $IFS
Nicht-Leerzeichen. $IFS
Leerzeichen(Leerzeichen, Tabulator, Zeilenumbruch)abgegrenzte Felder werden durchReihenfolgeauf ein einzelnes Feld(oder gar keine, wenn sie nicht vor etwas anderem stehen)- Also...
IFS=\ ; var=' '; printf '<%s>' $var
<>
Aber alle anderen sind so angegeben, dass sie ein einzelnes Feld auswertenPro Vorkommen- sie werden nicht gekürzt.
IFS=/; var='/////'; printf '<%s>' $var
<><><><><>
AlleVariablenerweiterungen sind standardmäßig $IFS
abgegrenzte Datenarrays - sie werden entsprechend in separate Felder aufgeteilt $IFS
. Wenn Sie "
eines in Anführungszeichen setzen, überschreiben Sie diese Arrayeigenschaft und werten es als einzelne Zeichenfolge aus.
Wenn ich das also tue ...
IFS=\"\'\`; set -- $var
Ich stelle das Argumentarray der Shell auf die vielen $IFS
abgegrenzten Felder ein, die durch die Erweiterung von generiert werden . Wenn es erweitert wird, sind $var
seine Bestandteile für die in enthaltenen Zeichen$IFS
verloren- sie sind jetzt nur noch Feldtrenner - sie sind \0NUL
.
"$*"
- wie andere Variablenerweiterungen in doppelten Anführungszeichen - überschreibt auch die Feldaufteilungseigenschaften von $IFS
. Aber,Zusätzlichersetzt es das erste Byte in$IFS
für jedes abgegrenzte Feldin "$@"
. Also, weil "
war dieErsteWert in$IFS
alle nachfolgenden Trennzeichen werden "
in "$*"
.Und es "
muss $IFS
auch nicht drin sein, wenn du es teilst. Du könntest ändern$IFS
nach set -- $args
auf einen ganz anderen Wert und seineneuDas erste Byte würde dann für die Feldtrennzeichen in angezeigt "$*"
. Darüber hinaus können Sie alle Spuren davon vollständig entfernen, wie:
set -- $var; IFS=; printf %s "$*"
AUSGABE
some crazy string here