Як замінити текст випадковим чином з файлу?


9

Як я можу випадково замінити конкретні рядки в одному текстовому файлі рядками з іншого файлу? Наприклад:

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
Це не випадково, схоже, ви не хочете, щоб щось повторювалося. Ви хочете, щоб це було насправді випадковим, або кожен рядок другого текстового файлу повинен використовуватися лише один раз? Також, чи потрібно це баш, або ви відкриті для інших інструментів?
тердон

1
@terdon Схоже, він хоче довільну перестановку (всі 5 елементів, але у випадковому порядку). Випадкова перестановка насправді випадкова, вам просто потрібно усунути вже обрані елементи при випадковому виборі наступного елемента. Іноді називають "випадковим родом"
thomasrutter

1
@thomasrutter так, я знаю, що це і моя відповідь. Але тому я просив ОП уточнити, оскільки як випадкова перестановка, так і випадковий вибір будуть розумними залежно від того, що їм потрібно.
тердон

Відповіді:


9

Якщо ви дійсно хочете випадкового вибору, то ось один із способів використання 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

ОТО, якщо ви хочете випадкову перестановку адрес, я б запропонував щось подібне

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
Приємно! Я розглядав, як це зробити, pasteале мені не спало на cutдумку видалити невідповідне поле.
тердон

2
Недоліком рішення для вставки є те, коли у file1 більше рядків, ніж у file2. Замість цього <(sort -R file2.txt)ми можемо використовувати щось подібне <(yes "$(<file2.txt)" | head -n $(wc -l < file1.txt) | sort -R)- це може перекрутити випадковість на користь рядків ближче до верхньої частини файлу2.
glenn jackman

10

Ви можете реалізувати цей алгоритм:

  • Завантажте вміст file2.txtмасиву
  • Для кожного рядка в file1.txt:
    • Витягніть частину імені
    • Отримайте випадкову адресу
    • Роздрукуйте результат правильно відформатованим

Подобається це:

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

(Особлива подяка @GlennJackman та @dessert за вдосконалення.)


3
Ви можете розглянути можливість заповнення масиву mapfile -t addresses < file2.txt- використовуючи catподібне, це підлягає розбиттю слів та розширенню імені файлу.
glenn jackman

2
Чи потрапляє цей останній не порожній рядок, file1.txtякщо цей файл не закінчується порожнім рядком (вибачте, наразі не можна перевірити)? Якщо ні, то я не рекомендую while IFS='' read -r orig || [[ -n "$orig" ]]; do, див. « Прочитати рядок файлу за рядком, присвоївши значення змінній · SO» .
десерт

2
@janos Щойно знайшов дуже гарне запитання на тему: сценарій Shell прочитав пропущений останній рядок
десерт

5

Ви можете використовувати shuf(можливо, вам потрібно sudo apt install shuf) перетасувати рядки другого файлу, а потім використовувати їх для заміни:

$ 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

shufпросто рандомізує порядок вхідних рядків. awkКоманда буде першою прочитати все file1 ( NR==FNRбуде тільки справедливо , коли перший файл читається), і зберігає друге поле (поля визначаються @, так що це домен) в асоціативному масиві a, значення якого є доменами і чиї ключі - номери рядків. Потім, коли ми перейдемо до наступного файлу, він просто надрукує все, що було збережено aдля цього номера рядка, а також те, що у файлі 2, для того ж номера рядка.

Зауважте, що це передбачає, що обидва файли мають однакову кількість рядків і насправді не є "випадковими", оскільки це не дозволить повторити щось. Але це схоже на те, що ви хотіли попросити.


5

Розчин Python 2.7 та 3

Це рішення замінює перше виникнення однієї довільної заданої рядки ("голки") у кожному рядку вхідного файлу рядком, щоразу вибирається випадковим чином із набору рядків списку рядків заміщення.

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

Пов’язати голку до початку або в кінці струни слід майже тривіально або взагалі використовувати регулярні вирази.

Використання

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

Приклад:

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

або

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

3

Ось стильний шлях:

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

Ще одне баш-рішення. Він використовує вбудовану функцію заміни рядків bash. Він також передбачає, що file2.txtмістить лише рядки заміни. Якщо ні, то їх можна спочатку відфільтрувати за допомогоюgrep -o <replace> file2.txt

З shuf

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

Без shuf(майже чисто bash)

Тут ми маємо спершу створити функцію, яка імітує shufтак

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

Тоді це схоже

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

Тест:

$ 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
$ 
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.