Wie ersetze ich Text zufällig aus einer Datei?


9

Wie kann ich bestimmte Zeichenfolgen in einer Textdatei zufällig durch Zeichenfolgen aus einer anderen Datei ersetzen? Zum Beispiel:

file1.txt(file has more than 200 lines):
moonwalker@address.com
hansolo@address.com
anakinskywalker@address.com
obiwankenobi@address.com
darthvader@address.com

file2.txt(file has 10-20 lines):
@adress1.com
@adress2.com
@adress3.com
@adress4.com
@adress5.com

output.txt:
moonwalker@address4.com
hansolo@address1.com
anakinskywalker@address5.com
obiwankenobi@address2.com
darthvader@address3.com

4
Das ist nicht zufällig, es sieht so aus, als ob Sie nicht wollen, dass etwas wiederholt wird. Möchten Sie, dass es tatsächlich zufällig ist, oder sollte jede Zeile der zweiten Textdatei nur einmal verwendet werden? Auch dann , wenn es brauchen bash zu sein, oder sind Sie zu anderen Tools öffnen?
Terdon

1
@terdon Es sieht so aus, als ob er eine zufällige Permutation will (alle 5 Elemente, aber in zufälliger Reihenfolge). Eine zufällige Permutation ist tatsächlich zufällig. Sie müssen nur die bereits ausgewählten Elemente entfernen, wenn Sie das nächste Element zufällig auswählen. Manchmal auch "zufällige Sortierung" genannt
Thomasrutter

1
@thomasrutter ja, das weiß ich und das macht meine Antwort. Aber deshalb habe ich das OP gebeten, dies zu klären, da sowohl eine zufällige Permutation als auch eine zufällige Auswahl sinnvoll wären, je nachdem, was sie benötigen.
Terdon

Antworten:


9

Wenn Sie wirklich eine zufällige Auswahl wünschen, haben Sie folgende Möglichkeiten awk:

awk '
  BEGIN{FS="@"; OFS=""} 
  NR==FNR{a[NR]=$0; n++; next} 
  {$2=a[int(1 + n * rand())]; print}
' file2.txt file1.txt
moonwalker@adress2.com
hansolo@adress2.com
anakinskywalker@adress5.com
obiwankenobi@adress1.com
darthvader@adress3.com

OTOH, wenn Sie eine zufällige Permutation der Adressen wünschen, würde ich so etwas vorschlagen

paste -d '' <(cut -d'@' -f1 file1.txt) <(sort -R file2.txt)
moonwalker@adress2.com
hansolo@adress1.com
anakinskywalker@adress5.com
obiwankenobi@adress4.com
darthvader@adress3.com

1
Nett! Ich wollte es damit machen, pasteaber es kam mir nicht in cutden Sinn, das nicht übereinstimmende Feld zu entfernen.
Terdon

2
Ein Nachteil der Einfügelösung ist, wenn Datei1 mehr Zeilen als Datei2 enthält. Stattdessen <(sort -R file2.txt)können wir so etwas wie verwenden <(yes "$(<file2.txt)" | head -n $(wc -l < file1.txt) | sort -R)- das kann die Zufälligkeit zugunsten von Zeilen verzerren, die näher am oberen Rand von Datei2 liegen.
Glenn Jackman

10

Sie könnten diesen Algorithmus implementieren:

  • Laden Sie den Inhalt von file2.txtin ein Array
  • Für jede Zeile in file1.txt:
    • Extrahieren Sie den Namensteil
    • Holen Sie sich eine zufällige Adresse
    • Drucken Sie die Ausgabe korrekt formatiert

So was:

mapfile -t addresses < file2.txt
while IFS='' read -r orig || [[ -n "$orig" ]]; do
    ((index = RANDOM % ${#addresses[@]}))
    name=${orig%%@*}
    echo "$name${addresses[index]}"
done < file1.txt

(Besonderer Dank geht an @GlennJackman und @dessert für die Verbesserungen.)


3
Sie könnten in Betracht ziehen, das Array mit zu füllen mapfile -t addresses < file2.txt- catwenn Sie dies verwenden , werden Sie einer Wortaufteilung und einer Dateinamenerweiterung unterzogen.
Glenn Jackman

2
Fängt dies die letzte nicht leere Zeile ab, file1.txtwenn diese Datei nicht mit einer leeren Zeile endet (leider kann ich sie derzeit nicht testen)? Wenn nicht, empfehle ich while IFS='' read -r orig || [[ -n "$orig" ]]; do, Zeile zeilenweise lesen und einer Variablen den Wert · SO zuweisen .
Nachtisch

2
@janos Gerade eine sehr gute Frage zum Thema gefunden: Shell-Skript lesen fehlende letzte Zeile
Dessert

5

Sie können shuf(möglicherweise müssen sudo apt install shuf) verwenden, um die Zeilen der zweiten Datei zu mischen und sie dann zu ersetzen:

$ awk -F'@' 'NR==FNR{a[NR]=$1;next}{print a[FNR]"@"$2} ' file1 <(shuf file2)
moonwalker@adress3.com
hansolo@adress1.com
anakinskywalker@adress5.com
obiwankenobi@adress4.com
darthvader@adress2.com

shufrandomisiert einfach die Reihenfolge seiner Eingabezeilen. Der awkBefehl dort liest zuerst die gesamte Datei1 ( NR==FNRist nur wahr, während die erste Datei gelesen wird) und speichert das zweite Feld (Felder werden durch definiert @, dies ist also die Domäne) im assoziativen Array, adessen Werte die Domänen und sind deren Schlüssel sind die Zeilennummern. Wenn wir dann zur nächsten Datei gelangen, wird einfach alles gedruckt, was afür diese Zeilennummer gespeichert wurde , zusammen mit dem, was in Datei 2 für dieselbe Zeilennummer enthalten ist.

Beachten Sie, dass dies voraussetzt, dass beide Dateien genau die gleiche Anzahl von Zeilen haben und nicht "zufällig" sind, da dadurch nichts wiederholt werden kann. Aber das sieht so aus, als wollten Sie danach fragen.


5

Python 2.7 und 3 Lösung

Diese Lösung ersetzt das erste Auftreten einer einzelnen beliebigen Zeichenfolge (die „Nadel“) in jeder Zeile der Eingabedatei durch eine Zeichenfolge, die jedes Mal zufällig aus dem Satz von Zeilen der Liste der Ersetzungszeichenfolgen ausgewählt wird.

#!/usr/bin/python
from __future__ import print_function
import sys, random

needle = sys.argv[1]

if sys.argv[2] == '-':
    f_replacements = sys.stdin
else:
    f_replacements = open(sys.argv[2])
with f_replacements:
    replacements = [l.rstrip('\n') for l in f_replacements]
if not replacements:
    raise ValueError('No replacement strings given')

if len(sys.argv) <= 3 or sys.argv[3] == '-':
    f_in = sys.stdin
else:
    f_in = open(sys.argv[3])
with f_in:
    for s in f_in:
        rep = replacements[random.randrange(len(replacements))]
        print(s.rstrip('\n').replace(needle, rep, 1))

Es sollte fast trivial sein, die Nadel am Anfang oder Ende der Schnur zu verankern oder reguläre Ausdrücke insgesamt zu verwenden.

Verwendungszweck

python replace-random.py NEEDLE REPLACEMENTS-FILE [INPUT-FILE]

Beispiel:

python replace-random.py '@address.com' file2.txt file1.txt

oder

python replace-random.py '@address.com' file2.txt < file1.txt

3

Hier ist ein Perl-Weg:

#!/usr/bin/perl
use warnings;
use strict;
use Tie::File;

tie my @file1,'Tie::File','file1.txt' or die "Can't open file1.txt\n";
tie my @file2,'Tie::File','file2.txt' or die "Can't open file2.txt\n";

for my $file_index (0..$#file1) {
   my $suffix = $file2[int(rand($#file2+1))];
   $file1[$file_index] =~ s/@.*$/$suffix/;
}

untie @file1;
untie @file2;

2

Eine andere Bash-Lösung. Es verwendet die in Bash integrierte Funktion zum Ersetzen von Zeichenfolgen. Es wird auch davon file2.txtausgegangen, dass nur die Ersatzzeichenfolgen enthalten sind. Wenn nicht, können sie zuerst mit gefiltert werdengrep -o <replace> file2.txt

Mit shuf

#search string
Search="@address.com"
for lines in $(grep $Search file1.txt)
do 
    echo ${lines/$Search/$(shuf file2.txt -n 1)} 
done

Ohne shuf(fast rein bash)

Hier müssen wir eine Funktion erstellen Sie zuerst , dass ahmt shufso gerne

bshuf () 
{ 
    nlines=$(( $(wc -l < $1) + 1))
    rand=0
    while [ "$rand" -eq 0 ]; do
        rand=$(( $RANDOM % nlines ))
    done
    echo $(head -n $rand $1 | tail -1)
}

Dann ist es ähnlich

for lines in $(grep $Search file1.txt) 
do 
    echo ${lines/$Search/$(bshuf file2.txt)}
done

Prüfung:

$ for lines in $(grep $Search file1.txt); do echo ${lines/$Search/$(bshuf file2.txt)} ; done
moonwalker@adress4.com
hansolo@adress2.com
anakinskywalker@adress2.com
obiwankenobi@adress3.com
darthvader@adress5.com
$ 
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.