2016-04-09 7 views
0

Ich muss bestimmte Zeilen in einer Textdatei bearbeiten. Ich habe hier ein Muster, pattern.txt:Wie verwenden Sie grep, um ein Muster in einer Datei zu finden, EDIT IT mit awk (oder etwas anderes), und speichern Sie es dann?

1 
3 
6 
17 
etc... 

und eine Datei mit Text, file.txt:

1 text 
2 text 
3 text 
4 text 
5 text 
etc... 

ich die Worte _PUT FLAG HERE bis zum Ende jeder Zeile von file.txt auf Linien hinzufügen möchten, das Spiel haben angezeigt durch die pattern.txt.

Ich habe

grep -F -f pattern.txt file.txt | awk '{print $0 "_PUT FLAG HERE" }' 

Aber ich kann nicht einen Weg zu Schubs diese Änderungen wieder in die ursprüngliche Datei scheinen, um herauszufinden, so dass es wie folgt aussieht:

1 text_PUT FLAG HERE 
2 text 
3 text_PUT FLAG HERE 
4 text 
5 text 
6 teeskjtkljeltsj _PUT FLAG HERE 
etc... 

Es ist ein viel wie versuchen, tr zu verwenden, aber viel mehr verschachtelte. Es sollte eine logische Möglichkeit geben, AWK und grep zu stringeln, ich kann mir einfach nicht vorstellen, wie man die Stücke zu einer einzigen Pipe zusammenfügt, und das kann ich nirgends finden. (Wenn Sie einen sed Weg erklären, dies zu tun, erklären Sie bitte die Regex.)

+2

Wenn Sie awk verwenden, können Sie Grep nicht mehr verwenden. Fast alles, was grep kann, kann awk auch tun. Keine Notwendigkeit für eine zusätzliche Rohrverbindung. Das heißt, können Sie vielleicht einige Meta-Code enthalten, der die Logik beschreibt, wie Sie dies vorstellen, sollte dies funktionieren? Ich verstehe es nicht aus Ihrer Beschreibung. – ghoti

+0

Siehe den dritten grauen Textblock? Ich muss es so aussehen. Sehen Sie, wie pattern.txt die Nummer 1 und 3 enthält? Es sagt mir, ich muss die Zeile mit der Nummer 3 oder 1 darin bearbeiten und die Zeichenfolge PUT_FLAG_HERE an das Ende der Muster anfügen, die pattern.txt entsprechen. – Tom

+1

Verwenden Sie niemals das Wort "pattern", da es sehr vieldeutig ist. Bitte editieren Sie Ihre Frage, um das Wort "pattern" durch "string" oder "regexp" (was auch immer Sie mit "pattern" meinen) überall dort zu verwenden, wo es gerade verwendet wird 'Muster.txt' und '137' in' file.txt'), damit wir sehen können, wie partielle Übereinstimmungen behandelt werden sollen. Schließen Sie auch Fälle wie '99 foo3bar' in' file.txt' ein, damit vorgeschlagene Lösungen, die nicht in der richtigen Spalte für '3' aussehen, fehlschlagen. –

Antwort

1

zu

sed -f <(sed 's#.*#/^& /s/$/_PUT FLAG HERE/#' pattern.txt) file.txt 

Erläuterung vereinfacht werden, Dies könnte für Sie arbeiten (GNU sed):

sed 's#.*#/&/s/$/_PUT FLAG HERE/#' pattern.txt | sed -f - file 

Dadurch wird die Pattern-Datei in ein sed-Skript, das dann auf die Textdatei aufgerufen wird.

+0

Sollte dies nicht Anker am Anfang der Zeile teilweise Übereinstimmungen zu vermeiden? –

+0

aus irgendeinem Grunde die anderen Antworten nicht funktioniert hat, aber dieses Wunder gearbeitet Ich werde es als die richtige Antwort auswählen, danke potong! – Tom

+0

Würde es Ihnen auch etwas ausmachen, mir zu erklären, was in all diesen Symbolen vor sich geht? Ich möchte diese Fähigkeit hinzufügen können, aber ich bin nicht in der Lage, die Kombination von Symboloperationen zu verstehen, weil ich nicht gewohnt bin, sed zu verwenden – Tom

2

awk zur Rettung!

Sie brauchen nicht auf andere Tools mit der vollen Leistung von awk zur Verfügung

$ awk -v tag='_PUT FLAG HERE' 'NR==FNR{a[$1];next} 
           {print $0 ($1 in a?tag:"")}' pattern file 

1 text_PUT FLAG HERE              
2 text 
3 text_PUT FLAG HERE 
4 text 
5 text 

nur als eine Übung, mit dem gleichen trete/Art

$ sort <(join pattern file --nocheck-order | 
     sed 's/$/_PUT_FLAG_HERE/') <(join -v2 pattern file --nocheck-order) 

1 text_PUT_FLAG_HERE 
2 text 
3 text_PUT_FLAG_HERE 
4 text 
5 text 

vielleicht Funktion definiert, für DRY

$ f() { join $1 pattern file --nocheck-order; }; sort <(f "" | 
         sed 's/$/_PUT_FLAG_HERE/') <(f -v2) 
+0

Ich bin immer wieder erstaunt, was 'awk' kann. Lasst uns das geben – Tom

3

Angenommen, Ihr Awk wurde als Geisel genommen.

Eine GNU Sed/Grep-Lösung! Um einen sed-Skript zu erzeugen, das tut, was Sie wollen, bekommen wir die Zeilen aus der Eingabedatei zu ändern:

$ grep -wFf pattern.txt file.txt 
1 text 
3 text 
6 text 
17 text 

Dieses ganzes Wort (-w), so wird 1 text abgestimmt, aber 11 text ist nicht; -F ist für feste Zeichenfolgen (keine Regex, sollte schneller sein) und -f pattern.txt liest die Muster aus einer Datei suchen.

Jetzt Rohr wir das sed ein Skript zu generieren:

$ grep -wFf pattern.txt file.txt | sed 's#.*#/^&$/s/$/_PUT FLAG HERE/#' 
/^1 text$/s/$/_PUT FLAG HERE/ 
/^3 text$/s/$/_PUT FLAG HERE/ 
/^6 text$/s/$/_PUT FLAG HERE/ 
/^17 text$/s/$/_PUT FLAG HERE/ 

Der sed Befehl in das Rohr passt die komplette Linie (.*) und montiert eine Adresse plus Substitution Befehl (& steht für das ganze vorher abgestimmt Linie).

Nun nehmen wir das alles und es als Eingabe verwenden für durch Prozess Substitution sed (erfordert Bash):

$ sed -f <(grep -wFf pattern.txt file.txt | sed 's#.*#/^&$/s/$/_PUT FLAG HERE/#') file.txt 
1 text_PUT FLAG HERE 
2 text 
3 text_PUT FLAG HERE 
4 text 
5 text 
6 text_PUT FLAG HERE 
7 text 
8 text 
9 text 
10 text 
11 text 
12 text 
13 text 
14 text 
15 text 
16 text 
17 text_PUT FLAG HERE 

Fertig!

Ja, ja, awk ist kürzer , schneller und schöner.


Eigentlich nicht, aber immerhin.

Noch eine Anmerkung: die grep Schritt nicht tatsächlich erforderlich ist, finden Antworten von Potong und Walter A.

+0

Das funktioniert nicht für mich. Mein tatsächlicher Fall ist radikal komplizierter als das falsche Beispiel, das ich gepostet habe. ABER ich bin mir unsicher, wo die Komplikation liegt, damit ich den richtigen Schnitt formulieren kann. Würde das funktionieren, wenn ich nur alle Leerzeichen gelöscht hätte? Und wie würdest du das in diesem Fall wiederholen? Ich denke, dass die Räume ich in meinem ursprünglichen Beispiel hinzugefügt werden, dies gemacht haben, viel einfacher als es sein sollte, und damit der Code, den Sie nicht gepostet den Vorgang nicht übereinstimmen Im wirklich – Tom

+0

tun @ Tom Wenn Sie Ihr Beispiel vereinfachen, so dass eine Lösung für das Beispiel löst das ursprüngliche Problem nicht, dann hast du zu viel vereinfacht;) Was genau meinst du mit "Eliminiere alle Leerzeichen"? Welche Leerzeichen? Wohlgemerkt, die Lösung von Walter A macht im Grunde dasselbe, aber direkter. –

2

starten:
pattern.txt:

1 
3 
6 
17 

datei.txt :

1 text 
2 text 
3 text 
4 text 
5 text 

Verwenden awk:

$ awk 'NR == FNR{seen[$1];next} $1 in seen{printf("%s_PUT FLAG HERE\n",$0);next}1' pattern.txt file.txt 

Output:

1 text_PUT FLAG HERE 
2 text 
3 text_PUT FLAG HERE 
4 text 
5 text 
2

Die Lösung kann @Benjamin

# Read awk commands from a file 
    sed -f awkcommands.txt pattern.txt file.txt 

# Read awk commands from other command 
    sed -f <(other_command) file.txt 

# Append string to every line by replacing end-of-line character $ 
    sed 's/$/_PUT FLAG HERE/' 

# Only append string on lines matching something 
    sed '/something/s/$/_PUT FLAG HERE/#' 

# Only append string on lines matching something at the beginning of the line followed by a space 
    sed '/^something /s/$/_PUT FLAG HERE/#' 

# Get the word something in above command selecting the whole line with .* and putting it in the new sed command with &. 
# The slashes are used for the inner sed command, so use # here 
    sed 's#.*#/^& /s/$/_PUT FLAG HERE/#' pattern.txt 

# Now all together: 
    sed -f <(sed 's#.*#/^& /s/$/_PUT FLAG HERE/#' pattern.txt) file.txt 
+0

Guter Punkt - wäre das nicht angemessener als Kommentar? –

+0

@BenjaminW. Guter Punkt auch - Ich habe zuerst etwas ohne Ihren Code versucht und meine Lösung mit Teilen von Ihnen optimiert. Ich dachte, du verdienst die Credits, deshalb habe ich dich erwähnt. Ich war mir nicht sicher, dass du deine Antwort bearbeiten würdest und ich wollte eine Alternative für das mächtige awk zeigen. –

+0

Ich bin immer für eine Sed Alternative zu awk zeigt, nur weil;) Ich habe meine Antwort jetzt nicht aktualisiert, so dass für doppelte Inhalte machen würde - nur bei Ihnen auch jetzt zu erwähnen. –

0

Diese Lösung verwendet nur Bash (4.0+) Features:

# Set up associative array 'patterns' whose keys are patterns 
declare -A patterns 
for pat in $(< pattern.txt) ; do patterns[$pat]=1 ; done 

# Slurp all the lines of 'file.txt' into the 'lines' array 
readarray -t lines < file.txt 

# Write each old line in the file, possibly with a suffix, back to the file 
for line in "${lines[@]}" ; do 
    read -r label text <<< "$line" 
    printf '%s%s\n' "$line" "${patterns[$label]+_PUT FLAG HERE}" 
done > file.txt 

NOTES:

  1. Die Änderungen geschrieben werden, zurück zu 'file.txt', wie die Frage zu geben scheint.
  2. Bash 4.0 oder höher ist für assoziative Arrays und readarray erforderlich.
  3. Bash ist sehr langsam, daher ist diese Lösung möglicherweise nicht praktisch, wenn eine der Dateien groß ist (mehr als 10 000 Zeilen).