¿Cómo reemplazar el texto al azar del archivo?


9

¿Cómo puedo reemplazar aleatoriamente cadenas específicas en un archivo de texto con cadenas de otro archivo? Por ejemplo:

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

44
Eso no es aleatorio, parece que no quieres que se repita nada. ¿Desea que sea realmente aleatorio, o cada línea del segundo archivo de texto solo debe usarse una vez? Además, ¿ debe ser bash o estás abierto a otras herramientas?
terdon

1
@terdon Parece que quiere una permutación aleatoria (los 5 elementos pero en un orden aleatorio). Una permutación aleatoria es realmente aleatoria, solo necesita eliminar los elementos ya elegidos al seleccionar aleatoriamente el siguiente elemento. A veces llamado "ordenamiento aleatorio"
thomasrutter

1
@ thomasrutter sí, lo sé y eso es lo que hace mi respuesta. Pero es por eso que le pedí al OP que aclarara, ya que tanto una permutación aleatoria como una selección aleatoria serían razonables dependiendo de lo que necesiten.
terdon

Respuestas:


9

Si realmente desea una selección aleatoria, entonces aquí hay una manera de usar 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 si quieres una permutación aleatoria de las direcciones, te sugiero algo como

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
¡Agradable! Estaba buscando hacerlo, pastepero no se me ocurrió usarlo cutpara eliminar el campo no coincidente.
terdon

2
Una desventaja de la solución de pegado es cuando el archivo1 tiene más líneas que el archivo2. En lugar de <(sort -R file2.txt)usar algo como <(yes "$(<file2.txt)" | head -n $(wc -l < file1.txt) | sort -R)eso, eso puede sesgar la aleatoriedad a favor de líneas más cercanas a la parte superior del archivo2.
Glenn Jackman

10

Podría implementar este algoritmo:

  • Cargue el contenido de file2.txten una matriz
  • Para cada línea en file1.txt:
    • Extrae la parte del nombre
    • Obtén una dirección aleatoria
    • Imprime la salida formateada correctamente

Me gusta esto:

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 agradecimiento especial a @GlennJackman y @dessert por las mejoras).


3
Puede considerar rellenar la matriz con mapfile -t addresses < file2.txt: el uso catcomo ese lo somete a división de palabras y expansión de nombre de archivo.
Glenn Jackman

2
¿Captura esto la última línea no vacía de file1.txtsi este archivo no termina con una línea vacía (lo siento, no puedo probar en este momento)? Si no lo recomiendo while IFS='' read -r orig || [[ -n "$orig" ]]; do, vea Leer un archivo línea por línea asignando el valor a una variable · SO .
postre

2
@janos Acabo de encontrar una muy buena pregunta sobre el tema: la secuencia de comandos de Shell no se encuentra en la última línea
postre

5

Puede usar shuf(puede que necesite sudo apt install shuf) para mezclar las líneas del segundo archivo y luego usarlas para reemplazar:

$ 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

shufsimplemente aleatoriza el orden de sus líneas de entrada. El awkcomando allí primero leerá todo el archivo1 ( NR==FNRsolo será verdadero mientras se lee el primer archivo) y guardará el segundo campo (los campos están definidos por @, por lo que este es el dominio) en la matriz asociativa acuyos valores son los dominios y cuyas claves son los números de línea. Luego, cuando lleguemos al siguiente archivo, simplemente imprimirá lo que esté almacenado apara este número de línea, junto con lo que está en el archivo 2 para el mismo número de línea.

Tenga en cuenta que esto supone que ambos archivos tienen exactamente el mismo número de líneas y que en realidad no son "aleatorios", ya que no permitirá que se repita nada. Pero eso se parece a lo que querías pedir.


5

Python 2.7 y 3 solución

Esta solución reemplaza la primera aparición de una sola cadena arbitraria dada (la "aguja") en cada línea del archivo de entrada con una cadena cada vez elegida al azar del conjunto de líneas de la lista de cadenas de reemplazos.

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

Debería ser casi trivial anclar la aguja al principio o al final de la cuerda o usar expresiones regulares por completo.

Uso

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

Ejemplo:

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

o

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

3

Aquí hay una manera 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

Otra solución bash. Utiliza la función de reemplazo de cadena incorporada bash. También supone que file2.txtcontiene solo las cadenas de reemplazo. Si no, se pueden filtrar primero usandogrep -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

Sin shuf(casi puro bash)

Aquí tenemos que crear primero una función que imite shufasí

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

Entonces es similar

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

Prueba:

$ 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
$ 
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.