Gibt es eine Möglichkeit, grep
zweimal in der Datei zu vermeiden und die Variablen nur in einem Durchgang zu füllen? Die Datei ist klein, es ist also keine große Sache, dass ich mich nur gefragt habe, ob ich es in einem Durchgang schaffen könnte
FIRST_NAME=$(grep "$customer_id" customer-info|cut -f5 -d,)
LAST_NAME=$(grep "$customer_id" customer-info|cut -f6 -d,)
bash
shell-script
shell
grep
Jim
quelle
quelle
Antworten:
Sie können einmal grep und zweimal teilen, indem Sie die Shell-Zeichenfolge ersetzen:
Oder mit Bash die Prozesssubstitution verwenden:
read
teilt die Eingabe aufIFS
und weist den ersten WertFIRST_NAME
und den Rest zuLAST_NAME
. Prozess - Substitution und Umleitung< <(...)
können Sie die Ausgabe von passierengrep ... | cut ...
zu ,read
ohne Verwendung eines Subshell.quelle
cut
Operation nach dem nicht ausgrep
und verwenden Sieread
, um die Zeichenfolge zu brechen, indem Sie eine benutzerdefinierte Angabe angebenIFS
und die benötigten Spalten aus einem Array abrufen.-f
- dies-fm,n
ist eine Liste von Feldern, kein Bereich -, funktioniert dies auch dann, wenn die Felder nicht nebeneinander liegen. Es erfordert, dass sie in Ordnung sind, aber wenn die Reihenfolge umgekehrt ist, tauschen Sie einfach die Variablen aus.read
in der Haupt-Shell, sodass die Variableneinstellung nicht verloren geht, während eine Pipelinesomething | read var var
diesread
in einer Unterschale abhängig von Ihrer Shell-Variante und manchmal Optionen / Modi tun kann .read
Befehl, nicht auf diegrep ... | cut
Pipeline, die sich in Bash immer in einer Subshell befindet, da es sich um eine Pipeline handelt.Am einfachsten wäre es, den gesamten Datensatz in eine Variable zu integrieren und diese dann zu verwenden
cut
.Auch persönlich würde ich empfehlen, einen spezifischeren regulären Ausdruck zu verwenden. Wenn sich Ihre Kunden-IDs immer am Zeilenanfang befinden, können Sie schreiben,
grep '^'"$customer_id"
anstattgrep "$customer_id"
zu verlangen, dass die Übereinstimmung am Zeilenanfang steht. Andernfalls können Sie Datensätze abrufen, bei denen Text, der mit der Kunden-ID übereinstimmt, an anderer Stelle im Datensatz angezeigt wird.quelle
Sie können
awk
in Kombination mit Bash verwendenread
:-F
Weist awk an, das Komma als Feldtrennzeichen zu verwenden-v
Setzt die awk-Variablecid
auf die Shell-Variable$customer_id
Wenn die Zeile mit der übereinstimmt
$customer_id
, druckt awk das 5. und 6. Feld und diesen werden die VariablenFIRST_NAME
und zugewiesenLAST_NAME
.Wenn Vorname ($ 5) je Raum enthält (Beispiel: a, b, c, d, Sarah Jane Smith) hinzufügen
-v OFS=,
habenawk
Ausgang Komma zwischen Feldern und Präfixread
mitIFS=,
ihm bei comma gespalten zu haben.Darüber hinaus
awk
kann nur in einem bestimmten Feld wie gesucht werden'$3~cid{print..}'
- und das gesamte Feld kann mit übereinstimmen,'$3~"^"cid"$"{print...}'
wenn dies für Ihre IDs von Bedeutung ist.quelle
/.../
und Ihrer Version$0~...
. Können Sie auch erklären, was bei Ihnen nicht funktioniert hat?cid
als Muster verwenden, stimmt awk nicht mit der Zeile mit der regulären Ausdrucks-CID überein. Es wird nur geprüft, ob die Variable cid nicht leer ist, obwohl dies immer der Fall ist. Daher gibt awk alle Zeilen aus, nicht die gewünschte einzelne Zeile. Deshalb müssen Sie$0 ~ cid
- die Linie ($ 0) mit dem regulären Ausdruck in cid abgleichen./regexp/ {action}
die Schrägstriche sind nicht Teil des regulären Ausdruck, sie sind spezielle Syntax , die sagt , es ist ein regulärer Ausdruck. Wenn Sie den regulären Ausdruck in einer Variablen setzen , wenn Sie einen Schrägstrich enthalten , dass Slash ist ein Datum Zeichen , das die Eingabedaten übereinstimmen muß (hier neben dem customer_id Wert) , die es wird mit ziemlicher Sicherheit nicht (obwohl möglicherweise könnte die OP - Daten hat gezeigt, dass hat immer Schrägstriche um die Werte von customer_id).Kleine Datei, große Datei. Eine Angewohnheit, die ich habe, besteht darin, Festplatten-E / A immer so weit wie möglich zu entfernen. Eine Möglichkeit, dies zu tun, besteht darin, die Datei in ein Array zu verschieben. Dies setzt natürlich voraus, dass env $ IFS für die Datei entsprechend eingestellt ist, eliminiert jedoch die E / A.
data=( $(cat customer-info) )
Dann können Sie daraus Kirsche pflücken ...
FIRST_NAME=$(echo "${data[@]}" | tr ' ' '\n' | grep "$customer_id" | cut -f5 -d,)
Eine andere Methode könnte darin bestehen, einem Array wie diesem nur die beiden gewünschten Bits zuzuweisen ...
data=( $(grep "${customer_id}" customer-info | cut -d, -f5,6) )
quelle
Die vorhandenen Antworten speichern alle die Ausgabe im Speicher (in einer Variablen) und spielen sie zweimal ab. Dies ist ein Problem, wenn Sie einen generischen Wrapper erstellen möchten, der eine beliebig große Eingabe annehmen und zwei Aufgaben ausführen kann. Stattdessen kann der Ausgabestream dupliziert und in zwei Befehle gestreamt werden.
In meinem Fall besteht der Zweck darin, sowohl den Header (erste Zeile) als auch eine bestimmte (Gruppe von) Zeile (n) in einem Ausgabestream zu filtern, der beliebig lang sein kann. Ein einfaches Beispiel wäre die Anzeige der Speicherplatznutzung:
Ersetzen Sie
df -h
mit dem Befehl , den Sie verwenden möchten, und ersetzenhead -1
undgrep '/$'
mit den beiden Befehlen möchten Sie sie anzuwenden. Die Ausgabe von beiden wird in Ihrem Terminal angezeigt, obwohl möglicherweise die Ausgabe des ersteren Befehls nach dem letzteren angezeigt wird.Wie funktioniert das?
tee
"[kopiert] die Standardeingabe in jedes [Argument] und auch in die Standardausgabe." So kann es die Ausgabe von stdin sowohl an stdout als auch an stderr senden, indem es verwendetcommand | tee /dev/stderr
.command >(command2)
Syntax wird durch ein Argument durch bash ersetzt undcommand /dev/fd/63
wird ausgeführt. Wenncommand
versucht wird, darauf zu schreiben/dev/fd/63
, landet es in der Eingabe (stdin) voncommand2
. Dies wird als Prozesssubstitution bezeichnet (sieheman bash
).tee
sowohl in das Argument (wir übergeben eine Befehlssubstitution als Argument) als auch in stdout geschrieben wird, können wir einfach eine weitere Pipe hinzufügen und einen weiteren Befehl ausführen. Also jetzt haben wircommand | tee >(command2) | command3
.command3
, würden wir (in meinem Beispiel) die Kopfzeile erfassen. Das wollen wir nicht: Wir wollen es anzeigen. Da wir stderr nicht durchleiten, ist die Umleitung der Ausgabe zu stderr eine einfache Möglichkeit, sie in unserem Terminal anzuzeigen, dh wir fügen hinzu>&2
, was dazu führtcommand | tee >(command2 >&2) | command3
.Es gibt ein Problem: Die Ausgabe kann in beliebiger Reihenfolge erfolgen. Abhängig von der kosmischen Strahlung können wir entweder das Obige oder das Folgende sehen:
Eine hackige, aber zuverlässige Möglichkeit, dies zu beheben (anstelle einer überentwickelten Methode, die nicht hackig ist), besteht darin, dem zweiten Befehl einen kurzen Ruhezustand hinzuzufügen. etwas wie:
Aber warten Sie , das bricht den zweiten Befehl (
grep
), weil jetzt die Ausgabe von geleitet wirdtee
zusleep
undgrep
wird für die Eingabe auf unbestimmte Zeit warten. Um dies zu beheben, fügen wir eine Unterschale hinzu:Jetzt wird die Ausgabe nicht zu,
grep
sondern zu unserer Subshell umgeleitet . Dasleep
es nicht daraus liest (es verbraucht den Stream nicht), steht es weiterhingrep
zum Lesen zur Verfügung. Jetzt funktioniert es zuverlässig, solange diehead
Ausgabe innerhalb von 0,01 Sekunden erfolgt (plus ein wenig Overhead auf der Grep-Seite). Dies ist eine faire Wette auf ein modernes System und kurz genug, um für den Benutzer nicht erkennbar zu sein.Da ich etwas machen wollte, das sowohl den Header als auch die Ausgabe eines Befehls benötigt, können wir dies verallgemeinern auf:
Da der
tee
Befehl in der Funktion nur von stdin liest und an stdout ausgibt, funktioniert dies genauso wie unser früherer Befehl außerhalb der Reihenfolge, wenn Sie ihn als verwendendf -h | grabheader | grep '/$'
. Aber da wir wollen, dass es in Ordnung ist, müssen wir es verzögern, es über den Standard zu senden:cat
hier wird nur sichergestellt, dass alles, was an den stdin übergeben wird, wieder auf den stdout gelangt. Wenn Sie keine Argumente übergeben und keine Umleitungen hinzufügen, wird genau das getan. Verwendungszweck:Im speziellen Fall von
df
kann dies natürlich viel einfacher gemacht werden:Aber jetzt haben wir eine allgemeine Möglichkeit, dies mit jedem Befehl zu tun.
quelle