python an der Unix shell mit cat command; umsetzten eines Perl Befehls nach Python

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
dark_universe
User
Beiträge: 7
Registriert: Dienstag 20. März 2018, 12:08

Hi Leute, ich bin neu hier und vesuche gerade mich in Python einzuarbeiten.
Könnte mir jemand helfen folgendes Perl command in Python umzubauen?
Zur Erklarung: Aus einem Text file sollen einfach die letzten Zeichen abgeschnitten werden, möchte aber kein Programm dazu schreiben sondern alles an der Unix shell machen. Das Perl-Commad lautet

cat Textfile.txt|perl -pe 's#(.*)(\d{6})#$1#' > Textfile.fixed

Das ist ( so ähnlich) im textfile eine Zeile und die letzen 6 Stellen aller Zeilen ( also das Datum: 201803 ) soll abgeschnitten/gelöscht werden:
12345656789345 5567 F345 123456400 0 0085201803

Wie würde ich das als Unix/Pyton Einzeiler schreiben?

Ich hoffe, Ihr könnt mir helfen

Gruß
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

@dark_universe: Python ist nicht ganz so kryptisch wie Perl, heißt, man muß da etwas ausführlicher programmieren. Weißt Du was Perl da macht? Das muß man dann durch explizites Lesen von der Standardeingabe und dem matchen eines Regulären Ausdrucks machen. Python-Einzeiler gibt es nicht.
Benutzeravatar
darktrym
User
Beiträge: 784
Registriert: Freitag 24. April 2009, 09:26

Schon Perl ist da zuviel, ein richtiger (schottischer) Administrator würde das in awk lösen.
„gcc finds bugs in Linux, NetBSD finds bugs in gcc.“[Michael Dexter, Systems 2008]
Bitbucket, Github
Benutzeravatar
DeaD_EyE
User
Beiträge: 1012
Registriert: Sonntag 19. September 2010, 13:45
Wohnort: Hagen
Kontaktdaten:

Ich denke das käme dem Perl Snippet am nächsten:

Code: Alles auswählen

import sys
import os
import re


regex = re.compile(r'.*(\d{6})$')

if not os.isatty(sys.stdin.fileno()):
    for line in sys.stdin:
        match = regex.search(line)
        if match:
            print(match.group(1))
Dann das Programm mit der Pipe verknüpfen:

Code: Alles auswählen

cat DATEI | python3 dein_filter.py
Mit awk wäre es kürzer. Neben awk gibt es noch nützliche tools wie z.B. cut und tr.
Ich bin damals vom Shell-Scripting weg, weil es mir zu viel Zeichensalat geworden ist.
Perl ist da nochmal eine Ecke kryptischer.
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
dark_universe
User
Beiträge: 7
Registriert: Dienstag 20. März 2018, 12:08

Damit müsste es doch gehen: <(...) denotes process substitution. Das kommt dem ziemlich nahe kommt was ich brauche:

script.py <(awk '{if ($4 == 1975) print $1,$2,$3}' input.txt)

- für script.py muss ein Python 3 da sein
- das <( leitet die Übergabe an ein Unix comand ein wie awk, grep oder sed
- in dem Bsp. werden alle Zeilen ausgegeben, die in der 4. Spalte ein 1975 haben

Allerdings scheitere ich schon daran, das spript.py aufzurufen. Python 3 liegt bei uns im /opt/.../python3 Verzeichnis.
Das inputfile sieht so aus:
D000001 D000001 44 1975
D000001 D000408 1 1983
D000001 D000641 1 1977
D000001 D000900 27 1975

Das awk komado alleine liefert
D000001 D000001 44
D000001 D000900 27
und der Python Teil schreibt das ganze in ein file? Hier brauche ich mal eure weitere Hilfe, damit mal das Bsp. läuft und dann gehts mit meinem Problem weiter.

Danke
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

Mit Python geht das auch kurz und knapp:

Code: Alles auswählen

import sys

for line in sys.stdin:
    sys.stdout.write(line.rstrip()[:-6] + '\n')
Angenommen obigen Code speicherst Du als 'fix.py' ab, dann führst Du das aus mit

'$ python fix.py < textfile.txt > textfile.fixed'

Falls Du die Shebang-Zeile gesetzt und die Datei als executable markiert hast, kannst Du auch die Extension weg lassen:

'./fix < textfile.txt > textfile.fixed'

Dies gilt für Dein erstes Beispiel, die letzten sechs Zeichen zu entfernen. Das kannst Du natürlich beliebig modifizieren oder auch gegen mögliche Fehler absichern.
dark_universe
User
Beiträge: 7
Registriert: Dienstag 20. März 2018, 12:08

Ok, vielen Dank, werde ich heute ausprobieren
dark_universe
User
Beiträge: 7
Registriert: Dienstag 20. März 2018, 12:08

So, hat geklappt, habe die Lsg von kbr mit line.rstrip()[:-6] umgestzt. Die konnte ich einfach nachvollziehen, das von DeaD_EyE war mir erst mal zu komplziert.
Danke auch an die anderen, die mir Wertvolle Tipps gegeben haben.
Gruß
sebastian0202
User
Beiträge: 168
Registriert: Montag 9. Mai 2016, 09:14
Wohnort: Berlin

Hallo,


mit sed geht das auch!
Wobei zu beachten ist, dass mit der Option -i, die Änderungen direkt übernommen werden.

[codebox=bash file=Unbenannt.bsh]
sed -i -r 's/(.*)[A-Z0-9 ]{6}$/\1/' test.txt
[/code]
dark_universe
User
Beiträge: 7
Registriert: Dienstag 20. März 2018, 12:08

Hallo nochmal,
bei genauem Betrachten der Lsg, von kbr mit line.rstrip()[:-6] gibt es noch 2 Probleme.

1. in jedem Zeilenende gibt es ein ^M ( 00123654321^M) das ich gerne erhalten möchte/muß oder am Ende wieder hinzufügen muss.

2. Jedes file das ich bearbeiten muss hat einen
a) Header,
b) dann kommen die Daten, wo jetzt die letzen 6 Zeichen gelöscht werden sollen
c) einen Trailer.

Header und Tailer sollen nicht verändert werden, die Datengröße oder besser die Anzahl der Zeilen dazwischen sind variabel, also mal 1000 Zeilen mal 50 000 Zeilen.

Wie würde ich da vorgehen? Erst mal die Zeilen der Datei zählen und dann erste und letzte Zeile von der Bearbeitung ausschließen?

Danke und Gruß
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

@dark_universe: das ^M kommt daher, dass Du DOS/Windows-Zeilenenden hast. Einfach statt '\n' -> '\r\n' schreiben. Bisher ignorierst Du ja Header und Trailer und löscht einfach die letzten 6 Ziffern, die Du in jeder Zeile findest (egal ob sie am Zeilenende stehen oder nicht). Sauberer wäre es dann tatsächlich, Header und Trailer gesondert zu behandeln. Woher erkennt man denn, was Header und was Trailer ist?
IHack
User
Beiträge: 14
Registriert: Dienstag 13. März 2018, 11:17

Code: Alles auswählen

sed -rn 's/(.+)[[:digit:]]{6}$/\1/;p' path/to/your/inputfile
Entfernt 6 dezimale Ziffern am Ende einer Zeile. Und nur, wenn es wirklich 6 sind.
Und sed sollte auf so ziemlich jedem *nix verfügbar sein.
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

@IHack: das funktioniert nicht, wenn, wie der OP sagt, ein Carriage-Return am Ende jeder Zeile steht. Einfacher wäre es dann auch, einfach nur die letzten 6 Zahlen durch nichts zu ersetzen:
[codebox=bash file=Unbenannt.sh]
sed -rn 's/[[:digit:]]{6}\s*$//;p' path/to/your/inputfile[/code]
dark_universe
User
Beiträge: 7
Registriert: Dienstag 20. März 2018, 12:08

@Sirius3:
Header und Trailer erkennt man nicht so einfach. Man weiss das sie am Anfang/Ende sind.

Deswegen würde ich wie folgt vorgehen für sagen wir mal 1000 files mit jeweils 100 - 100 000 Zeilen:
1. File1 öffnen und und Zeilen zählen: erste Zeile ist n, letzte nMax -> erste Schleife
2. for- Schleife mit dem Script vonb kbr:
import sys

for line in sys.stdin:
sys.stdout.write(line.rstrip()[:-6] + '\n')


und in der Schleife von n+1 bis nMax-1 laufen lassen
3. beide Schleifen schliessen
4. File2 öffnen und weiter gehts ...

Könnt Ihr mir mal helfen, wie dazu das äussere Phyton Programmier-Gerüst auszusehen hat, das wäre klasse.
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

@dark_universe: Ob header und tailer einfach oder schwer zu erkennen sind, ist völlig egal. Wenn sie vorhanden sind, musst Du sie erkennen. Sonst kannst Du keine Fallunterscheidung bezüglich der zu bearbeitenden Zeilen treffen.
Wenn Du weißt, dass der header genau eine Zeile beträgt, dann ist das kein Problem – Du überspringst die Zeile einfach. Beim trailer ist das anders: da brauchst Du eine Mustererkennung. Oder, falls der trailer auch nur eine Zeile beträgt, pufferst Du die zu bearbeitende Zeile zunächst, um zu sehen, ob noch eine weitere nachfolgt. Eigentlich eine prima Übung.
Antworten