Come sostituire il testo in modo casuale dal file?


9

Come posso sostituire casualmente stringhe specifiche in un file di testo con stringhe di un altro file? Per esempio:

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
Non è casuale, sembra che tu non voglia ripetere nulla. Vuoi che sia effettivamente casuale, o ogni riga del secondo file di testo dovrebbe essere usata una sola volta? Inoltre, deve essere bash o sei aperto ad altri strumenti?
Terdon,

1
@terdon Sembra che voglia una permutazione casuale (tutti i 5 elementi ma in un ordine casuale). Una permutazione casuale è in realtà casuale, devi solo eliminare gli elementi già scelti quando selezioni in modo casuale l'elemento successivo. A volte chiamato "ordinamento casuale"
thomasrutter il

1
@thomasrutter sì, lo so ed è quello che fa la mia risposta. Ma è per questo che chiedevo al PO di chiarire poiché sia ​​una permutazione casuale che una scelta casuale sarebbero state ragionevoli a seconda di ciò di cui avevano bisogno.
terdon,

Risposte:


9

Se vuoi davvero una selezione casuale, ecco un modo usando 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 se vuoi una permutazione casuale degli indirizzi, suggerirei qualcosa di simile

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
Bello! Stavo cercando di farlo con pastema non mi è venuto in mente di usare cutper rimuovere il campo non corrispondente.
Terdon,

2
Un aspetto negativo della soluzione incolla è quando file1 ha più righe di file2. Invece di <(sort -R file2.txt)usare qualcosa del genere <(yes "$(<file2.txt)" | head -n $(wc -l < file1.txt) | sort -R), ciò potrebbe distorcere la casualità in favore di linee più vicine alla cima di file2.
Glenn Jackman,

10

È possibile implementare questo algoritmo:

  • Carica il contenuto di file2.txtin un array
  • Per ogni riga in file1.txt:
    • Estrarre la parte del nome
    • Ottieni un indirizzo casuale
    • Stampa l'output formattato correttamente

Come questo:

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

(Un ringraziamento speciale a @GlennJackman e @dessert per i miglioramenti.)


3
Potresti prendere in considerazione la possibilità di popolare l'array con mapfile -t addresses < file2.txt- usando in catquesto modo soggetti alla suddivisione delle parole e all'espansione del nome file.
Glenn Jackman,

2
Questo rileva l'ultima riga non vuota file1.txtse questo file non termina con una riga vuota (scusate, al momento non è possibile eseguire il test)? In caso contrario while IFS='' read -r orig || [[ -n "$orig" ]]; do, consultare Leggere un file riga per riga assegnando il valore a una variabile · SO .
Dessert,

2
@janos Ho appena trovato un'ottima domanda sull'argomento: script Shell ha letto l'ultima riga mancante
dessert il

5

È possibile utilizzare shuf(potrebbe essere necessario sudo apt install shuf) per riordinare le righe del secondo file e quindi utilizzarle per sostituire:

$ 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

shufsemplicemente randomizza l'ordine delle sue linee di input. Il awkcomando lì prima leggerà tutto il file1 ( NR==FNRsarà vero solo mentre il primo file viene letto) e salva il secondo campo (i campi sono definiti da @, quindi questo è il dominio) nell'array associativo i acui valori sono i domini e le cui chiavi sono i numeri di riga. Quindi, quando arriviamo al file successivo, stamperà semplicemente tutto ciò che è stato memorizzato aper questo numero di riga, insieme a ciò che è nel file 2 per lo stesso numero di riga.

Si noti che ciò presuppone che entrambi i file abbiano esattamente lo stesso numero di righe e che in realtà non sia "casuale", poiché non consentirà di ripetere nulla. Ma sembra quello che volevi chiedere.


5

Soluzione Python 2.7 e 3

Questa soluzione sostituisce la prima occorrenza di una singola stringa data arbitraria (l '"ago") in ogni riga del file di input con una stringa ogni volta scelta casualmente dall'insieme di righe dell'elenco delle stringhe di sostituzioni.

#!/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))

Dovrebbe essere quasi banale ancorare l'ago all'inizio o alla fine della corda o usare del tutto le espressioni regolari.

uso

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

Esempio:

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

o

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

3

Ecco un modo perl:

#!/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

Un'altra soluzione bash. Utilizza la funzione di sostituzione della stringa integrata bash. Presuppone inoltre che file2.txtcontenga solo le stringhe di sostituzione. In caso contrario, possono essere prima filtrati utilizzandogrep -o <replace> file2.txt

Con shuf

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

Senza shuf(quasi puro bash)

Qui dobbiamo prima creare una funzione che imiti in questo shufmodo

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

Quindi è simile

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

Test:

$ 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
$ 
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.