Как заменить текст случайно из файла?

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
elanozturk
источник
4
Это не случайно, похоже, вы не хотите ничего повторять. Вы хотите, чтобы это было фактически случайно, или каждая строка второго текстового файла должна использоваться только один раз? Кроме того, это должен быть Bash, или вы открыты для других инструментов?
Тердон
1
@terdon Похоже, он хочет случайную перестановку (все 5 элементов, но в случайном порядке). Случайная перестановка на самом деле случайная, вам просто нужно исключить уже выбранные элементы при случайном выборе следующего элемента. Иногда называют «случайной сортировкой»
томасрутер
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

OTOH, если вы хотите случайную перестановку адресов, я бы предложил что-то вроде

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
steeldriver
источник
1
Ницца! Я пытался сделать это с, pasteно мне не пришло в голову, чтобы использовать cutдля удаления несоответствующего поля.
Тердон
2
Один недостаток решения для вставки - это когда файл1 имеет больше строк, чем файл2. Вместо этого <(sort -R file2.txt)мы можем использовать что-то вроде <(yes "$(<file2.txt)" | head -n $(wc -l < file1.txt) | sort -R)- это может исказить случайность в пользу строк ближе к вершине файла2.
Гленн Джекман
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таким образом вы подвергаетесь разделению слов и расширению имени файла.
Гленн Джекман
2
Отлавливает ли это последнюю непустую строку, file1.txtесли этот файл не заканчивается пустой строкой (извините, не могу проверить в данный момент)? Если нет, я рекомендую while IFS='' read -r orig || [[ -n "$orig" ]]; do, см. Чтение файла построчно, присваивая значение переменной · SO .
десерт
2
@janos Только что нашел очень хороший вопрос по теме: « Сценарий оболочки прочитал пропущенную последнюю строку
десерт
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команда сначала прочитает весь NR==FNRфайл file1 ( будет истинно только во время чтения первого файла) и сохранит второе поле (поля определены @, так что это домен) в ассоциативном массиве a, значениями которого являются домены и чьи ключи являются номерами строк. Затем, когда мы перейдем к следующему файлу, он просто напечатает все, что было сохранено aдля этого номера строки, вместе с тем, что находится в файле 2 для того же номера строки.

Обратите внимание, что это предполагает, что оба файла имеют одинаковое количество строк и на самом деле не являются «случайными», так как не допускают повторения чего-либо. Но это похоже на то, что вы хотели попросить.

terdon
источник
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

Вот способ 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

Другое решение Bash. Он использует встроенную функцию замены строк в 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
$ 
SigmaPiEpsilon
источник