Erhalten Sie Werte eines Abschnitts einer Datei


7

Ich habe eine Konfigurationsdatei im folgenden Format.

<Title>
 [part1]
  A.File = a
  A.Val = val1
  B.File = a
  B.Val = val1
 [part2]
  A.File = a1
  A.Val = val2 
  B.File = a
  B.Val = val1

Ich möchte nur Werte aus dem ersten Teil extrahieren.

 #!/bin/sh 
getCalibDate()
{
 file="/path/of/config/file"
 value=`cat ${file} | grep Val | cut -d'=' -f2`
    for v in $value
    do
            echo $v
    done
}
getCalibDate

Das obige Skript gibt alle Werte zurück. Wie kann ich nur Werte aus dem ersten Teil (Teil 1) erhalten?


5
Beachten Sie, dass dies cat file | grep foosinnlos ist. Es ist bekannt als "nutzlose Verwendung von Katze" . Sie können grep foo filestattdessen immer tun .
Terdon

Sagen ${file}statt nur einfach $fileist gelegentlich nützlich, aber es ist viel nützlicher, sich daran zu gewöhnen, zu sagen "$file". Das Sprichwort ${file}schützt Sie nicht vor Leerzeichen oder Platzhalterzeichen $file.
G-Man sagt 'Reinstate Monica'

Antworten:


8

Wenn Sie nur 4 Zeilen haben [part1], können Sie die -A4Option verwenden mit grep:

cat ${file} | grep -A4 "part1" | cut -d'=' -f2`

Für den allgemeinen Fall (mehr als 4 Zeilen nach [Teil1]) verwenden Sie sed, um den Text zwischen zwei Teilen abzurufen :

cat ${file} | sed -n "/part1/,/part2/p" | head -n-1

headist part2am Ende zusätzlich zu löschen

Wie Terdon sagte, müssen Sie nicht verwenden cat, können Sie stattdessen Folgendes tun:

grep -A4 "part1" ${file} | cut -d'=' -f2`

ODER:

sed -n "/part1/,/part2/p" ${file} | head -n-1

4

Sie müssen ein komplexeres Tool verwenden, um die Datei zu analysieren. Zum Beispiel awk:

#!/bin/sh 

getCalibDate()
{
 file="${1}"
 value=$(awk  '/\[part/{a++}(a<2 && /Val/){print $NF}' ${file})

    for v in $value
    do
            echo $v
    done
}

getCalibDate ${1}

Hier wird die Variable ajedes Mal erhöht, wenn eine Zeile übereinstimmt [part. Dann wird das letzte Feld ( $NF) gedruckt, wenn eine Zeile übereinstimmt, Valaber nur, wenn aes kleiner als 2 ist, nur wenn wir uns im 1. Abschnitt befinden.


awkist einfach ein erstaunliches Werkzeug
Nidal

2
@Networker ja. Es ist eine vollständige Programmiersprache, in der einige sehr, sehr komplexe Programme geschrieben sind awk. Zum Beispiel einer der ersten Algorithmen zur Genvorhersage.
Terdon

Die Frage lautet cut -d'=' -f2, was das =zweitbegrenzte Feld ergibt . Ihre Antwort ergibt das letzte durch Leerzeichen getrennte Feld. Sie sind für die Beispieldaten in der Frage gleich, es handelt sich jedoch um Beispieldaten . Ich frage mich, wie die realen Daten aussehen.
G-Man sagt "Reinstate Monica"

@ G-Man stimmt. Ich hatte ursprünglich geschrieben, awk -F= '{print $2}'was dasselbe tut, aber ich fand, dass $NFdies in diesem Fall einfacher war.
Terdon

@terdon: Genvorhersage? Das ist ein bisschen ... awk
user541686

2

und benutze dies:

sed -n -e '/\[part1\]/,/\[part2\]/p' FILE |sed -e '1d;$d'| awk -F "=" '{print $2}'

AUSGABE ist:

 a
 val1
 a
 val1

2

So erhalten Sie die gesamten Zeilen aus dem ersten Teil:

awk '$1 ~ /^\[/ {n++;next} n==1'

So drucken Sie einfach die rechte Seite der ersten =:

awk '$1 ~ /^\[/ {n++;next} n==1 {sub(/^[^=]*=[[:blank:]]*/,""); print}'

2

Hier gibt es einige gute Antworten, aber ich sehe nur eine, die den ValTeil des Problems enthält, und es ist nicht eindeutig, ob dies richtig ist. Ich bin damit einverstanden, dass dies awk„ein erstaunliches Werkzeug“ ist, aber hier ist es nicht notwendig. Ich glaube, dass dieser sedBefehl:

sed -n '/\[part1\]/,/\[part2\]/s/.*Val.*=//p' "$file"

wahrscheinlich macht was gewünscht. Wie die anderen sed -e '/\[part1\]/,/\[part2\]/p'Lösungen ( Networker und Babyy ) ist dies trivial anpassbar, um einen beliebigen Abschnitt auszuwählen. (Sie müssen natürlich den Namen kennen. Wenn Sie nur die Ordnungszahl kennen, können Sie die Antwort von terdon oder die Antwort von glenn jackman anpassen. Beide zählen Abschnitte, anstatt nach einem bestimmten Namen zu suchen.) Wenn Sie dies nicht tun Kennen Sie den Namen des folgenden Abschnitts, können Sie tun

sed -n '/ \ [part42 \] /, / \ [part / s…' "$ file"

zum Beispiel.

Meine einzige Meta-Frage betrifft den cut -d'=' -f2Teil der Frage. Wenn eine Eingabezeile, aus der wir Daten extrahieren, mehrere =Zeichen nach enthält Val(dh der Feldwert enthält =Zeichen), z.

Einstein.Val = E=mc^2

dann cutextrahiert der obige Befehl nur den Text zwischen dem ersten und dem zweiten = (dh dem Feldwert bis zum ersten (aber nicht einschließlich) dem ersten =), z  E. Der sedBefehl, den ich oben dargestellt habe, extrahiert nur den Text nach dem letzten = (z mc^2. B. ). Verwenden Sie, um alles nach dem ersten =(z. B. E=mc^2) zu erhalten

sed -n '/\[part1\]/,/\[part2\]/s/.*Val[^=]*=//p' "$file"

Verwenden Sie, um das Verhalten von cut(z. B.  E) nachzuahmen

sed -n '/\[part1\]/,/\[part2\]/s/.*Val[^=]*=\([^=]*\).*/\1/p' "$file"

Beachten Sie, dass mein Ansatz davon ausgeht, dass die Daten zumindest allgemein der Abbildung in der Frage entsprechen. dh mindestens eine =erscheint irgendwo rechts von der ValZeichenfolge. Dementsprechend werden alle meine Lösungen Eingaben wie ignorieren

Girl.Name = Valerie
Valerie Bertinelli

auch wenn es zwischen [part1]und fällt [part2].


Ihre letzte Sache ahmt das Schnittverhalten nicht nach. Wenn cutTeilungen gleich sind und nur das zweite Feld der Zeile gedruckt werden und - wie Sie wahrscheinlich vorschlagen - mehr als übereinstimmende Felder vorhanden sind, behandelt Ihr altes das Problem der letzten drei übereinstimmenden Felder. Und so ahmt Ihr Befehl nicht nach cut.
Mikeserv

@mikeserv: Huh? Was?
G-Man sagt "Reinstate Monica"

genau das, was ich gesagt habe - Es ahmt keinen Schnitt nach, weil Sie es tun s/.*... Wenn Sie dies tun, wird nur das letztmögliche Vorkommen von erhalten Val[^=]*=\([^=]*\).*/\1. Scheint, als hättest du es nur dafür viel ausgearbeitet.
Mikeserv

@mikeserv: "genau das, was ich gesagt habe"? Ernsthaft? "... es gibt mehr als passende Felder als deine alten ..." ist ein Schritt weiter als Kauderwelsch - ich hoffe, da ist ein Tippfehler drin, aber ich kann nicht erraten, was du meinst. "Behandelt das Problem der letzten drei übereinstimmenden Felder" ist nur geringfügig verständlicher. Meinen Sie damit, dass mein Befehl für Zeilen mit " Val…=…Val…=" fehlschlägt ? Wenn ja, haben Sie wohl Recht. Ich habe diesen Fall nicht berücksichtigt. Aber in Ihrem ersten Kommentar gibt es nichts, was darauf hindeutet, dass Sie darüber sprechen, und Ihr zweiter Kommentar ist noch lange nicht klar.
G-Man sagt "Reinstate Monica"

@mikeserv: Denken Sie daran, ich sagte: "Beachten Sie, dass mein Ansatz davon ausgeht, dass die Daten zumindest allgemein wie die Abbildung in der Frage aussehen." Wenn Sie Anspruch gehen , dass meine Lösung nicht auf Eingabe , dass Blicke radikal verschieden von der Darstellung in der Frage, obliegt es Sie zumindest ein Beispiel von Eingabe für die mein Befehl fehlschlägt.
G-Man sagt "Reinstate Monica"

0
sed -n '/part2/q;s/[^=]*=//p' \
<<\DATA
<Title>
 [part1]
  A.File = a
  A.Val = val1
  B.File = a
  B.Val = val1
 [part2]
  A.File = a1
  A.Val = val2
  B.File = a
  B.Val = val1
DATA

AUSGABE

 a
 val1
 a
 val1

Das sollte den Trick machen. qDie Eingabedatei wird sofort angezeigt, wenn sie zum ersten Mal irgendwo in der Eingabe auf die Zeichenfolge part2 trifft . Dies bedeutet, dass niemals versucht wird, die Teile der Datei zu lesen, die Sie nicht möchten - was sie sehr effizient machen sollte.

Das -ndeaktiviert das automatische Drucken, sodass sednur das gedruckt wird, was definitiv zum Drucken aufgefordert wird p. Das einzige Mal, dass es definitiv zum Drucken aufgefordert wird, ist, wenn eine Folge von 0 oder mehr Zeichen, die nicht vorhanden sind, =und einem Zeichen, das ein Zeichen ist, erfolgreich entfernt werden kann =.

Wenn Sie stattdessen die gesamte übereinstimmende Zeile drucken möchten, können Sie Folgendes tun:

sed -n '/part2/q;/=/p'
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.