Wie liest man eine Eingabedatei mit Zeichenfolgen, gleicht sie ab und ändert die Übereinstimmungen direkt vor Ort?

Wie liest man eine Eingabedatei mit Zeichenfolgen, gleicht sie ab und ändert die Übereinstimmungen direkt vor Ort?

Ich habe eine Textdatei mit Zeichenfolgen/Dateinamen in separaten Zeilen, z. B. filename.txt. Es gibt Hunderte von Dateinamen

ABC123_S386_R1_001
JKL345_S441_R1_001
filename9000_S587_R1_001

und eine weitere Textdatei mit den Zeichenfolgen/Dateinamen und zusätzlichen Daten, z. B results.txt.:

>ABC123_S386_R1_001 
NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN
>JKL345_S441_R1_001
NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN
>abc7890_S387_R1_001  
NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN
>filename9000_S587_R1_001
NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN

Nun filename.txtsind nicht alle Dateinamen in vorhanden und auch nicht in der richtigen Reihenfolge. Ich möchte allen Dateinamen von bis results.txtein Präfix hinzufügen, aber nicht den anderen.filename.txtresults.txt

Wie lese ich eine Eingabedatei mit Zeichenfolgen, vergleiche sie mit einer anderen Datei und ändere die Übereinstimmungen?

Früher habe ich einzelne Dateinamen mit abgeglichen sequence.txt, ihre Zeilennummer ermittelt und sedmit Zeilennummern entweder eine einzelne Zeile oder einen Zeilenblock geändert.

Meine gewünschte Ausgabe würde so aussehen

>h-19/US/CA-ABC123_S386_R1_001 
NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN
>h-19/US/CA-JKL345_S441_R1_001
NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN
>abc7890_S387_R1_001  
NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN
>h-19/US/CA-filename9000_S587_R1_001
NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN

wo h-19/US/CA-ist das Suffix, das ich allen Übereinstimmungen hinzufügen möchte.

Bearbeiten: >ist das erste Zeichen aller Zeichenfolgen, die geändert werden müssen. Vor dem Dateinamen stehen keine Zeichen >und nach dem Dateinamen sind auch keine nachstehenden Leerzeichen zu sehen.

Antwort1

Vorausgesetzt, dass die relevanten Zeilen results.txtnach dem Dateinamen keine Leerzeichen enthalten, awkfunktioniert das folgende Programm:

awk -v prefix="h-19/US/CA-" 'NR==FNR{fnames[$1]; next} \
    /^>/{name=substr($0,2); if (name in fnames) {sub(/^>/, ">" prefix)} }1' filenames.txt results.txt
  • Dadurch wird zuerst analysiert filenames.txtund dann results.txt.
  • Während der Analyse filenames.txt(wobei FNRder Zeilenzähler pro Datei gleich NRdem globalen Zeilenzähler ist) werden alle Dateinamen (die die einzigen Felder in der Zeile sind) in einem Array registriert fnames, die Ausführung dann aber sofort zur nächsten Zeile übersprungen.
  • Beim Parsen results.txtwird geprüft, ob eine Zeile mit beginnt >. Wenn ja, wird geprüft, ob die Teilzeichenfolge, die diesem Zeichen folgt (vorübergehend in gespeichert name), unter den „Array-Indizes“ von gefunden wird fnames. Wenn dies der Fall ist, wird sub()das führende >durch +das Präfix ersetzt, das als Variable >übergeben wurde (über die Direktive).awkprefix-v
  • Der scheinbare „Verirrte“ 1weist an, awkdie aktuelle Zeile einschließlich aller möglichen Änderungen auszudrucken (aber nur, weil results.txtwir diesen Teil bei der Verarbeitung der ersten Datei nicht erreichen).

Beachten Sie, dass awkes selbst keine Dateien direkt ändern kann, Sie müssen also mit einer temporären Datei arbeiten. Wenn Sie eine ausreichend neue Version von GNU Awk (> 4.1.0) haben, können Sie die Erweiterung jedoch verwenden ; natürlich müssen Sie dann die Option für die Datei inplacedeaktivieren :filenames.txt

awk -i inplace -v prefix=" ... " ' ... ' inplace=0 filenames.txt inplace=1 results.txt

Dadurch wird die direkte Bearbeitung für deaktiviert filenames.txtund für wieder aktiviert results.txt.

Antwort2

Mit sedkönnen Sie die Dateinamen im Haltebereich sammeln und dann alle Zeilen auf results.txtÜbereinstimmungen prüfen, um herauszufiltern, welche Zeilen geändert werden sollen:

sed -e '1,/^$/{H;1h;d;}' -e 'G;/^>\(.*\).*\n\1\n/s_^>_>h-19/US/CA-_;P;d' filename.txt <((echo)) results.txt
  • <((echo))Ihr seht, dass ich zwischen den Dateien eine Leerzeile mitgebe , die 1,/^$/alle Zeilen der ersten Datei (und die Leerzeile) anspricht.
  • Diese Zeilen werden an den Haltebereich angehängt und dann gelöscht H;1h;d( 1hvermeidet, den Haltebereich mit einer neuen Zeile zu beginnen).
  • Ghängt den Haltebereich an alle Zeilen von an result.txtund /^>\(.*\).*\n\1\n/gleicht die Zeilen ab, die mit und einer Zeichenfolge beginnen >, die ein Dateiname ist (im Haltebereich in Zeilenumbrüche eingeschlossen)
  • s_^>_>h-19/US/CA-_ersetzt diese Zeile
  • P;ddruckt nur die erste Zeile ohne den angehängten Müll. Sie könnten s/\n.*//stattdessen

Antwort3

perlFür direkte Änderungen an der Eingabedatei verwenden :

pfx='h-19/US/CA-' \
perl -pi -e '
  BEGIN { %h = map { tr/\n//dr => $ENV{pfx}} <STDIN>}
  s/^>\K(?=(.*))/$h{$1}/;
' results.txt < filename.txt

verwandte Informationen