Comment remplacer le texte au hasard du fichier?

9

Comment remplacer au hasard des chaînes spécifiques dans un fichier texte par des chaînes d'un autre fichier? Par exemple:

file1.txt(file has more than 200 lines):
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]

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

output.txt:
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
elanozturk
la source
4
Ce n'est pas aléatoire, on dirait que vous ne voulez rien répéter. Voulez-vous qu'il soit réellement aléatoire, ou chaque ligne du deuxième fichier texte ne doit-elle être utilisée qu'une seule fois? Aussi, est - il besoin d'être bash, ou êtes - vous ouvrir à d' autres outils?
terdon
1
@terdon On dirait qu'il veut une permutation aléatoire (tous les 5 éléments mais dans un ordre aléatoire). Une permutation aléatoire est en fait aléatoire, il vous suffit d'éliminer les éléments déjà choisis lors de la sélection aléatoire de l'élément suivant. Parfois appelé "tri aléatoire"
thomasrutter
1
@thomasrutter oui, je le sais et c'est ce que fait ma réponse. Mais c'est pourquoi je demandais au PO de clarifier, car une permutation aléatoire et un choix aléatoire seraient raisonnables en fonction de ce dont ils ont besoin.
terdon

Réponses:

9

Si vous voulez vraiment une sélection aléatoire, voici une façon d'utiliser 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 vous voulez une permutation aléatoire des adresses, je suggère quelque chose comme

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
tournevis
la source
1
Agréable! Je cherchais à le faire avec, pastemais je n'ai pas pensé à utiliser cutpour supprimer le champ non correspondant.
terdon
2
Un inconvénient de la solution de collage est lorsque file1 a plus de lignes que file2. Au lieu de cela, <(sort -R file2.txt)nous pouvons utiliser quelque chose comme <(yes "$(<file2.txt)" | head -n $(wc -l < file1.txt) | sort -R)- cela peut fausser le caractère aléatoire en faveur de lignes plus proches du haut du fichier2.
glenn jackman
10

Vous pouvez implémenter cet algorithme:

  • Charger le contenu de file2.txtdans un tableau
  • Pour chaque ligne dans file1.txt:
    • Extraire la partie du nom
    • Obtenez une adresse aléatoire
    • Imprimer la sortie correctement formatée

Comme ça:

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 merci spécial à @GlennJackman et @dessert pour les améliorations.)

janos
la source
3
Vous pourriez envisager de remplir le tableau avec mapfile -t addresses < file2.txt- en utilisant catcomme cela vous soumet au fractionnement de mots et à l'expansion du nom de fichier.
glenn jackman
2
Est-ce que cela intercepte la dernière ligne non vide de file1.txtsi ce fichier ne se termine pas par une ligne vide (désolé, impossible de tester pour le moment)? Sinon, je recommande while IFS='' read -r orig || [[ -n "$orig" ]]; do, voir Lire un fichier ligne par ligne en attribuant la valeur à une variable · SO .
dessert
2
@janos Je viens de trouver une très bonne question sur le sujet: Script shell lu dernière ligne manquante
dessert
5

Vous pouvez utiliser shuf(vous devrez peut-être sudo apt install shuf) pour mélanger les lignes du deuxième fichier, puis les utiliser pour remplacer:

$ 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

shufrandomise simplement l'ordre de ses lignes d'entrée. La awkcommande y lira d'abord tout le fichier1 ( NR==FNRne sera vraie que pendant la lecture du premier fichier) et enregistre le deuxième champ (les champs sont définis par @, c'est donc le domaine) dans le tableau associatif adont les valeurs sont les domaines et dont les clés sont les numéros de ligne. Ensuite, lorsque nous arrivons au fichier suivant, il imprimera simplement tout ce qui a été stocké apour ce numéro de ligne, ainsi que ce qui se trouve dans le fichier 2 pour le même numéro de ligne.

Notez que cela suppose que les deux fichiers ont exactement le même nombre de lignes et ne sont pas réellement "aléatoires", car cela ne permettra rien de se répéter. Mais cela ressemble à ce que vous vouliez demander.

terdon
la source
5

Solution Python 2.7 et 3

Cette solution remplace la première occurrence d'une seule chaîne donnée arbitraire («l'aiguille») dans chaque ligne du fichier d'entrée par une chaîne à chaque fois choisie aléatoirement dans l'ensemble des lignes de la liste des chaînes de remplacement.

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

Il devrait être presque trivial d'ancrer l'aiguille au début ou à la fin de la chaîne ou d'utiliser des expressions régulières.

Usage

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

Exemple:

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

ou

python replace-random.py '@address.com' file2.txt < file1.txt
David Foerster
la source
3

Voici un moyen 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;
Josh
la source
2

Une autre solution bash. Il utilise la fonction de remplacement de chaîne intégrée bash. Il suppose également que file2.txtcontient uniquement les chaînes de remplacement. Sinon, ils peuvent d'abord être filtrés à l'aide degrep -o <replace> file2.txt

Avec shuf

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

Sans shuf(presque pur bash)

Ici, nous devons d'abord créer une fonction qui imite shufcomme

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

Alors c'est pareil

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

Tester:

$ 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
$ 
SigmaPiEpsilon
la source