Kürzeste Möglichkeit, Zeichen in einer Variablen zu entfernen

Kürzeste Möglichkeit, Zeichen in einer Variablen zu entfernen

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 trLö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 sednatü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 OUTPUTVariable 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, trist eindeutig am schnellsten, dicht gefolgt von sed. Außerdem scheint die Verwendung von echotatsä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 trist 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. trWenn 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 dashnormalerweise 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 $IFSund erfolgen $*.

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

AUSGABE

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

Hier ist printfes, damit Sie es sehen können. Aber natürlich hätte ich Folgendes gemacht:

var="$*"

...anstatt des printfBefehlswerts $varwäre das, was Sie dort in der Ausgabe sehen.

Wenn ich set -fdie 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 +fwelcher Zeile auch immer ich es möchte.

Die Feldaufteilung erfolgt basierend auf den Zeichen in $IFS.

Es gibt zwei Arten von $IFSWerten: $IFSLeerzeichen und $IFSNicht-Leerzeichen. $IFSLeerzeichen(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 $IFSabgegrenzte 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 $IFSabgegrenzten Felder ein, die durch die Erweiterung von generiert werden . Wenn es erweitert wird, sind $varseine Bestandteile für die in enthaltenen Zeichen$IFSverloren- 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 $IFSauch nicht drin sein, wenn du es teilst. Du könntest ändern$IFS nach set -- $argsauf 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

verwandte Informationen