6 Stimmen

Bash oder Python zum Rückwärtsgehen?

Ich habe eine Textdatei mit vielen zufälligen Vorkommen der Zeichenkette @STRING_A, und ich wäre daran interessiert, ein kurzes Skript zu schreiben, das nur einige von ihnen entfernt. Insbesondere eines, das die Datei durchsucht und sobald es eine Zeile findet, die mit dieser Zeichenfolge beginnt, wie

@STRING_A

prüft dann, ob 3 Zeilen zurück eine weitere Zeile vorkommt, die mit der gleichen Zeichenkette beginnt, etwa

@STRING_A

@STRING_A

und wenn es passiert, das Vorkommen 3 Zeilen rückwärts zu löschen. Ich dachte an Bash, aber ich weiß nicht, wie man damit "rückwärts" geht. Ich bin mir also sicher, dass dies mit Bash nicht möglich ist. Ich habe auch an Python gedacht, aber dann müsste ich alle Informationen im Speicher ablegen, um rückwärts gehen zu können, und dann wäre es bei langen Dateien nicht machbar.

Was meinen Sie dazu? Ist es möglich, es in Bash oder Python zu tun?

Gracias

1voto

goger Punkte 590

Wie AlbertoPL sagte, speichern Sie Zeilen in einem Fifo für spätere Verwendung - gehen Sie nicht "rückwärts". Dafür würde ich auf jeden Fall Python statt Bash+sed/awk/whatever verwenden.

Ich habe mir ein paar Minuten Zeit genommen, um dieses Snippet zu programmieren:

from collections import deque
line_fifo = deque()
for line in open("test"):
    line_fifo.append(line)
    if len(line_fifo) == 4:
        # "look 3 lines backward"                                               
        if line_fifo[0] == line_fifo[-1] == "@STRING_A\n":
            # get rid of that match
            line_fifo.popleft()
        else:
            # print out the top of the fifo
            print line_fifo.popleft(),
# don't forget to print out the fifo when the file ends
for line in line_fifo: print line,

0voto

jkerian Punkte 15571

Meine awk-Kenntnisse waren noch nie so gut... aber das Folgende könnte Ihnen das bieten, wonach Sie in Form einer bash-shell/shell-utility suchen:

sed `awk 'BEGIN{ORS=";"}
/@STRING_A/ {
  if(LAST!="" && LAST+3 >= NR) print LAST "d"
  LAST = NR
}' test_file` test_file

Im Grunde... erzeugt awk einen Befehl für sed, um bestimmte Zeilen zu entfernen. Ich bin sicher, es gibt einen relativ einfachen Weg, um awk tun alle der Verarbeitung, aber dies scheint zu funktionieren.

Das Schlimme daran? Es liest die test_file zweimal.

Das Gute daran? Es ist eine Bash/Shell-Utility-Implementierung.

Edit: Alex Martelli weist mich darauf hin, dass die obige Beispieldatei mich verwirrt haben könnte (mein obiger Code löscht die gesamte Zeile und nicht nur das @STRING_A-Flag)

Dies lässt sich leicht beheben, indem der Befehl in sed geändert wird:

sed `awk 'BEGIN{ORS=";"}
/@STRING_A/ {
  if(LAST!="" && LAST+3 >= NR) print LAST "s/@STRING_A//"
  LAST = NR
}' test_file` test_file

0voto

John Machin Punkte 78125

Diese "Antwort" ist für Lyrae ... Ich ändere meinen vorherigen Kommentar: Wenn sich die Nadel in den ersten drei Zeilen der Datei befindet, wird Ihr Skript entweder einen Indexfehler verursachen oder auf eine Zeile zugreifen, auf die es nicht zugreifen sollte, manchmal mit interessanten Nebeneffekten.

Beispiel für ein Skript, das IndexError verursacht:

>>> lines = "@string line 0\nblah blah\n".splitlines(True)
>>> needle = "@string "
>>> for i,line in enumerate(lines):
...     if line.startswith(needle) and lines[i-3].startswith(needle):
...         lines[i-3] = lines[i-3].replace(needle, "")
...
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
IndexError: list index out of range

und dieses Beispiel zeigt nicht nur, dass die Erde rund ist, sondern auch, warum Ihre "Lösung" für das Problem "nicht die ganze Zeile löschen" folgendermaßen hätte aussehen sollen .replace(needle, "", 1) o [len(needle):] 代わりに .replace(needle, "")

>>> lines = "NEEDLE x NEEDLE y\nnoddle\nnuddle\n".splitlines(True)
>>> needle = "NEEDLE"
>>> # Expected result: no change to the file
... for i,line in enumerate(lines):
...     if line.startswith(needle) and lines[i-3].startswith(needle):
...         lines[i-3] = lines[i-3].replace(needle, "")
...
>>> print ''.join(lines)
 x  y   <<<=== whoops!
noddle
nuddle
        <<<=== still got unwanted newline in here
>>>

-1voto

Andrew Punkte 1823

In der Bash können Sie verwenden sort -r filename y tail -n filename um die Datei rückwärts zu lesen.

$LINES=`tail -n filename | sort -r`
# now iterate through the lines and do your checking

-1voto

sqram Punkte 6791

Das könnte das sein, wonach Sie suchen?

lines = open('sample.txt').readlines()

needle = "@string "

for i,line in enumerate(lines):
    if line.startswith(needle) and lines[i-3].startswith(needle):
        lines[i-3] = lines[i-3].replace(needle, "")
print ''.join(lines)

diese Ausgaben:

string 0 extra text
string 1 extra text
string 2 extra text
string 3 extra text
--replaced --  4 extra text
string 5 extra text
string 6 extra text
@string 7 extra text
string 8 extra text
string 9 extra text
string 10 extra text

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X