2016-03-27 7 views
1

Hier ist das Problem: ich habe ~ 35k-Dateien, die möglicherweise oder nicht ein enthalten könnten oder mehrere der Strings in einer Liste von 300 Zeilen jeweils einen regulären Ausdruck enthältVerwendung von Grep + Sed basierend auf einer Musterdatei?

wenn ich grep -rnwl 'C:\out\' --include=*.txt -E --file='comp.log' ich sehe, gibt es ein paar Tausende von Dateien, die enthält eine Übereinstimmung.

jetzt wie bekomme ich sed, um jede Zeile in diesen Dateien zu löschen, die die Zeichenketten in comp.log zuvor verwendet?

edit: comp.log enthält eine einfache regex in jeder Zeile, aber zum größten Teil jeder Saite abgestimmt werden ist einzigartig

dies ist ein Beispiel dafür, wie es strukturiert ist:

server[0-9]\/files\/bobba fett.stw 
[a-z]+ mochaccino 
[2-9] CheeseCakes 
... 

usw. alberne Beispiele beiseite, es geht zu zeigen, dass jede Zeile einzigartig ist, abgesehen von ein paar Variationen, so sollte es nicht beeinflussen, was ich wirklich will: sehen, ob irgendwelche dieser Zeilen mit den Zeilen in der Datei an arbeiten. Es ist nicht anders als 's/pattern/replacement /', außer dass ich die Muster in der Datei anstelle von Inline verwenden möchte.


hier Ok ein Update (SO stationären bekommt, wenn ich die Frage nach ein paar Tagen beantwortet nicht erklären) nach viel Hantieren mit dem @ Kenavoz/@ Fischer Ansatz, fand ich eine ganz andere Lösung, aber das wichtigste zuerst. Erstellen einer modifizierten Musterliste für sed mit arbeiten funktioniert arbeiten.

sowie @ werkritter Ansatz von sed insgesamt ablegen. (diesen finde ich am meisten ... äh ... "am wenigsten gewunden" um das Problem herum).

ich nicht @ Mklement Antwort Arbeit unter Windows/Cygwin machen konnte (es tat unter ubuntu auf Arbeit, also ... nicht sicher, was das bedeutet. Zahlen.)

Was das Problem in eine Lösung endete mehr ... langfristige, wiederverwendbare Form war ein wunderbares Programm von einem Kollegen namens PowerGrep. es weht wirklich jede andere Option aus dem Wasser. Leider ist es nur Windows und es ist nicht kostenlos. (nicht einmal Werbung hier, das Ding ist nicht billig, aber es löst das Problem).

Wenn man bedenkt, dass die Antwort @Werkiters keine "richtige" Antwort war und ich nicht nur @Lars Fischer und @ Kenavoz 'Antwort als Lösung wählen kann (sie ergänzen sich), belohne ich @Kenavoz das Häkchen dafür zuerst.

abschließende Gedanken: Ich hatte auf eine einfachere, universelle und freie Lösung gehofft, aber anscheinend gibt es nicht.

+1

ersetzen funktioniert Was ist ' 'C: \ out \ ''? Sind Sie auf einem Windows/DOS-Rechner? – anubhava

+0

oder könnte ich einfach den Dateipfad und die Zeilennummer von grep übergeben, anstatt mit der Musterdatei vergleichen zu müssen ... wieder? – VileTouch

+0

Ja, es ist Windows, aber wenn ich statt CMD Pfefferminz öffne, sieht es anders aus, also denke ich nicht, dass es relevant ist. "c: \ out" ist der Pfad, in dem sich alle Dateien befinden, die geändert werden sollen. – VileTouch

Antwort

2

Sie können dies versuchen:

sed -f <(sed 's/^/\//g;s/$/\/d/g' comp.log) file > outputfile 

Alle regex in comp.log formatiert sind, auf eine Sed-Adresse mit einem d Befehl: /regex/d. Dieser Befehl löscht Linien, die mit den Mustern übereinstimmen.

Dieser interne sed wird als Datei (mit process substitition) an die Option -f der externen sed gesendet, die auf file angewendet wird.

sed -f <(sed 's/^/s\//g;s/$/\/\/g/g' comp.log) file > outputfile 

Update::

Um nur String-Matching, die Muster (nicht alle Zeilen) zu löschen

Die Befehlsausgabe an outputfile umgeleitet wird.

+0

warten, wenn ich richtig verstehe, ist Ihre Ausgabedatei comp.log das, s nicht die Datei, die ich bearbeitet werden will, dass; s die Liste der Zeichenfolgen in den Dateien übereinstimmen, effektiv was (ich nehme an) ist Ihre Regexlist . jedoch scheinen Sie sed in sed verwendet zu haben, um eine temporäre Datei auszugeben, die ... eine Zeile enthält? – VileTouch

+0

Aktualisiert. Intern sed transformiert Ihre Regex-Datei in sed-formatierte Muster. Bei der Prozesssubstitution wird es als reguläre Regex-Datei verwendet, die auf 'Datei' angewendet wird. – SLePort

2

Einige Ideen, aber keine vollständige Lösung, da es etwas Anpassung an Ihr Skript erfordert (nicht in der Frage gezeigt).

  1. Ich würde comp.log in ein sed-Skript konvertieren die notwendigen Löschungen enthält:

    cat comp.log | sed -r "s+(.*)+/\1/ d;+" > comp.sed` 
    

    Das Ihr Beispiel würde comp.sed wie folgt aussehen:

    /server[0-9]\/files\/bobba fett.stw/ d; 
    /[a-z]+ mochaccino/ d; 
    /[2-9] CheeseCakes/ d; 
    
  2. dann I würde das Skript comp.sed auf jede von grep gemeldete Datei anwenden (mit Ihrer -rnwl, die eine Filterung erfordern würde, um den Dateinamen zu erhalten.):

    sed -i.bak -f comp.sed $AFileReportedByGrep 
    

    Wenn Sie Gnu sed haben, können Sie -i inplace Ersatz verwenden, um eine BAK-Backup erstellen, ansonsten verwenden Rohrleitungen in eine temporäre Datei

+0

Das ist ein [nutzloser Gebrauch von 'Katze'] (http://www.iki.fi/era/unix/award.html), aber gute Kommentare im Allgemeinen. – tripleee

+0

Hast du meine Antwort gelesen, bevor du deine geschrieben hast? – SLePort

+0

Nun, es gibt kein Skript. Ich habe mit einer .bat-Datei mit einer for-Schleife begonnen und sie zugunsten von grep gelöscht, da sie zu langsam und ineffizient war. Also, was Sie sehen, ist alles, was es gibt. Ja, mein sed akzeptiert den Parameter -i, ich denke, das ist, was Cygwin mit sich bringt. – VileTouch

0

Beide Kenavoz's answer und Lars Fischer's answer die gleiche geniale Ansatz verwenden:
wandelt die Liste der Eingabe-Regexes in eine Liste von sed Match-and-Delete-Befehlen um, die als eine Datei, die als Skript fungiert, an sed über -f übergeben werden.

Um diese Antworten mit einem einzigen Befehl zu ergänzen, dass sie alle zusammen bringt, Sie GNU angenommen habensed und Ihre Schale ist bash, ksh oder zsh (<(...) zu unterstützen):

find 'c:/out' -name '*.txt' -exec sed -i -r -f <(sed 's#.*#/\\<&\\>/d#' comp.log) {} + 
  • find 'c:/out' -name '*.txt' entspricht allen *.txt Dateien im Teilbaum von dir. c:/out

    • -exec ... + gibt wie viele passende Dateien auf einem einzigen Befehlszeile zu dem angegebenen Befehl passen, in der Regel nur in einer einzigen Invokation resultiert.
  • sed -i aktualisiert die Eingabedateien in-place (konzeptuell gesprochen - es gibt Einschränkungen); fügen Sie ein Suffix (z. B. -i.bak) an, um Sicherungen der Originaldateien mit diesem Suffix zu speichern.

  • sed -r aktiviert Unterstützung für erweitert regulären Ausdrücken, was die Eingabe Regexes sind.

  • sed -f liest das Skript aus dem angegebenen Dateinamen ausführen, die in diesem Fall, wie in Kenavoz's answer erklärt, ein Prozess Substitution (<(...)) verwendet den beigefügten sed Befehl des Ausgang wirkt wie eine [transiente] Datei zu machen.

    • Der s///sed Befehl - die # alternative Trennzeichen verwendet Einsatz von literal/ zu erleichtern - umschließt jede Zeile von comp.log/\<...\>/d in den gewünschten Löschbefehl zu erhalten; das Einschließen des Eingabe-Regex in \<...\> gewährleistet das Übereinstimmen als ein Wort, wie grep -w tut.
      Dies ist der Hauptgrund, warum GNUsed erforderlich ist, weil weder POSIX EREs (reguläre Ausdrücke erweitert) noch BSD/OSX sed Unterstützung \< und \>.
      • aber Sie konnte es mit BSD/OSX sed von -r mit -E und \</\> mit [[:<:]]/[[:>:]]
Verwandte Themen