Besserer Weg, um “echo $ x | sed… “und„ echo $ x | grep ... "

14

Ich finde das oft in Skripten (und ich muss zugeben, ich schreibe es selbst):

a=$(echo "$x" | sed "s/foo/bar/")

oder

if echo "$x" | grep -q foo
then
    ...
fi

Betrachten Sie "foo", um einige Regex-Sachen aufzunehmen.

Ich bin der Meinung, dass es einen besseren Weg geben sollte - und höchstwahrscheinlich auch gibt -, der nicht zwei Befehle und eine Pipe beinhaltet, sondern das Ding in einen kompakteren Ausdruck einwickelt.

Ich kann es einfach nicht finden. Irgendjemand?

DevSolar
quelle
Ich gehe davon aus, dass dieser Ausdruck aufgrund einer Kombination aus Ignoranz (keine Alternativen kennen) und Wartbarkeit (Alternativen kennen, aber dies als einfacher zu verstehen wählen) häufig verwendet wird. Ich kann auf einen Blick erkennen, was Ihre Beispiele bewirken, aber ich benötige eine Shell-Referenz, um die Alternativen in den Antworten von Grawity und Dan McG herauszufinden.
Quacksalber Quijote
3
Übrigens ist die bevorzugte Methode zum $()Ersetzen von Befehlen eher Backticks als Backticks.
Bis auf weiteres angehalten.
2
Es ist auch eine gute Idee, Erweiterungen anzugeben, damit Leerzeichen geschützt sind: a="$(echo "$x" | sed "s/foo/bar/")"und if echo "$x" | grep foo; ….
Chris Johnsen
Gute Bemerkungen zu $ ​​() vs. ``. Ich sehe, dass meine Bash-Fähigkeiten noch nicht so gut sind.
DevSolar

Antworten:

12

Wenn Sie nicht von einer bestimmten Shell ausgehen, gibt es keinen besseren Weg, dies zu tun als "Pipe Echo to Tool" (oder nur ein "Tool" selbst wie expr ). Mit der traditionellen Bourne-Shell und / oder der POSIX-Shell können Sie sich wirklich auf alles verlassen . Wenn Sie andere Muscheln in Betracht ziehen, gibt es einige andere integrierte Möglichkeiten.

ksh hat

  • zusätzliche Muster: ?(pattern-list), *(pattern-list), {n}(pattern-list), {n,m}(pattern-list), @(pattern-list), !(pattern-list);
  • der %P printf- Bezeichner zum Konvertieren eines erweiterten regulären Ausdrucks in ein Muster (und %Rzum Erweitern eines erweiterten regulären Ausdrucks in ein Muster);
  • der expr == patternZustand in [[ expr ]]Tests;
  • die ${param/pattern/replacement}Parametererweiterung.

Bash hat

  • die extglobOption, die meisten zusätzlichen Muster von ksh (no {n}und {n,m}) zu aktivieren ;
  • der expr == patternZustand (in [[ expr ]]Tests);
  • die ${param/pattern/replacement}Parametererweiterung;
  • (in neueren Versionen) die expr =~ extregexpBedingung (in [[ expr ]]Tests), die mit erweiterten regulären Ausdrücken übereinstimmen kann
    • Mit in Klammern gesetzten Unterausdrücken und dem BASH_REMATCHParameter könnten Ersetzungen im sed- Stil durchgeführt werden.

zsh hat

  • seine eigenen erweiterten Muster mit der EXTENDED_GLOBOption;
  • ksh- ähnliche erweiterte Muster mit der KSH_GLOBOption;
  • der expr == patternZustand (in [[ expr ]]Tests);
  • die ${pattern/pattern/replacement}Parametererweiterung;
  • die expr =~ extregexpBedingung (in [[ expr ]]Tests), die mit erweiterten regulären Ausdrücken übereinstimmen kann,
    • Es kann PCRE anstelle von einfachen erweiterten regulären Ausdrücken verwenden, wenn die Option RE_MATCH_PCRE festgelegt ist.
    • Mit in Klammern gesetzten Unterausdrücken, dem MATCHParameter und dem matchParameter (oder BASH_REMATCHmit dem BASH_REMATCHOptionssatz) könnten Ersetzungen im sed- Stil durchgeführt werden.
  • das zsh/pcreModul, das bietet pcre_compile, pcre_studyund pcre_matchBefehle und die -pcre-match Testbedingung (in [[ expr ]]Tests);
  • das zsh/regexModul, das die -regex-match Testbedingung bietet (in [[ expr ]]Tests).
Chris Johnsen
quelle
Beeindruckend. So vollständig, wie man es sich nur wünschen kann. Auf jeden Fall ein Gewinner.
DevSolar
6

Gehen Sie wie folgt vor, um die sed-Leitung zu ersetzen

${a/foo/bar} oder ${a//foo/bar}

In der ersten Form wird nur die erste Instanz ersetzt. Das zweite Formular ist ein globales Suchen und Ersetzen.

In Ihrem Fall wäre es

Anstatt:

if echo $x | grep foo
then
    ...
fi

Erwägen Sie die Verwendung von:

if [ $x =~ foo ]
then
    ...
fi

Wo fooist ein regulärer Ausdruck.

Dan McGrath
quelle
In welchen Muscheln funktioniert das?
Peltier
1
if [ $x =~ foo ]gibt hier eine Fehlermeldung aus. if [[ $x =~ foo ]]funktioniert jedoch hervorragend. Vielen Dank!
DevSolar
1
All dies sind Bashismen, sie erfordern Bash. Darüber hinaus =~erfordert bash> = 3 iirc.
inkaphink
1
Außerdem ist foo in diesen Beispielen kein regulärer Ausdruck (wie in PCRE), sondern verwendet Platzhalter. Weitere Informationen zu dieser Bash-Güte finden Sie in der Bash-Manpage im Abschnitt "Parametererweiterung". Ich schätze besonders Dinge wie ${a##foo}und ${a%%bar}.
inkaphink
3
Der Übereinstimmungsoperator =~verwendet reguläre Ausdrücke. Dies gilt zum Beispiel: [[ "abcdddde" =~ ^a.*d+.g* ]](das sind zum Beispiel null oder mehr gs anstelle von g-Platzhaltern).
Bis auf weiteres angehalten.
2

Eine gute posix-kompatible Methode zum Testen, ob eine Variable ein Muster enthält, ist:

test ${var##*foo*} || <do something>;

Die Syntax der Parametererweiterung lautet:

 ${parameter##pattern}

wobei patternein Shell - Muster .

mrucci
quelle