Bagaimana cara mengganti teks secara acak dari file?


9

Bagaimana saya bisa secara acak mengganti string tertentu dalam satu file teks dengan string dari file lain? Sebagai contoh:

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
Itu tidak acak, sepertinya Anda tidak ingin apa pun diulang. Apakah Anda ingin benar-benar acak, atau haruskah setiap baris file teks kedua hanya digunakan sekali? Juga, apakah perlu bash, atau Anda terbuka untuk alat lain?
terdon

1
@terdon Sepertinya dia ingin permutasi acak (semua 5 elemen tetapi dalam urutan acak). Permutasi acak sebenarnya acak, Anda hanya perlu menghilangkan elemen yang sudah dipilih saat memilih elemen berikutnya secara acak. Kadang-kadang disebut "semacam acak"
thomasrutter

1
@ Thomas, ya, saya tahu itu dan itulah jawaban saya. Tapi itu sebabnya saya meminta OP untuk mengklarifikasi karena permutasi acak dan pilihan acak akan masuk akal tergantung pada apa yang mereka butuhkan.
terdon

Jawaban:


9

Jika Anda benar - benar menginginkan pilihan acak, maka inilah salah satu cara menggunakan 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 jika Anda ingin permutasi acak alamat, saya akan menyarankan sesuatu seperti

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
Bagus! Saya ingin melakukannya pastetetapi tidak terpikir oleh saya cutuntuk menghapus bidang yang tidak cocok.
terdon

2
Satu kelemahan dari solusi paste adalah ketika file1 memiliki lebih banyak baris daripada file2. Alih-alih <(sort -R file2.txt)kita dapat menggunakan sesuatu seperti <(yes "$(<file2.txt)" | head -n $(wc -l < file1.txt) | sort -R)- yang dapat membelokkan keacakan dalam mendukung garis lebih dekat ke bagian atas file2.
glenn jackman

10

Anda dapat menerapkan algoritma ini:

  • Memuat konten file2.txtke sebuah array
  • Untuk setiap baris di file1.txt:
    • Ekstrak bagian nama
    • Dapatkan alamat acak
    • Cetak hasilnya dengan benar diformat

Seperti ini:

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

(Terima kasih khusus kepada @GlennJackman dan @dessert untuk perbaikannya.)


3
Anda mungkin mempertimbangkan mengisi array dengan mapfile -t addresses < file2.txt- menggunakan catseperti itu subjek Anda untuk pemisahan kata dan ekspansi nama file.
glenn jackman

2
Apakah ini menangkap baris non-kosong terakhir file1.txtjika file ini tidak diakhiri dengan baris kosong (maaf, tidak dapat menguji saat ini)? Jika tidak saya sarankan while IFS='' read -r orig || [[ -n "$orig" ]]; do, lihat Baca file per baris yang menetapkan nilai ke variabel · SO .
hidangan penutup

2
@janos Baru saja menemukan pertanyaan yang sangat bagus tentang topik: Script shell membaca baris terakhir yang hilang
makanan penutup

5

Anda dapat menggunakan shuf(Anda mungkin perlu sudo apt install shuf) untuk mengocok baris file kedua dan kemudian menggunakannya untuk mengganti:

$ 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

shufcukup mengacak urutan jalur inputnya. The awkperintah sana pertama akan membaca semua file1 ( NR==FNRhanya akan menjadi kenyataan sedangkan file pertama adalah menjadi read), dan menyimpan kolom kedua (bidang didefinisikan oleh @, jadi ini adalah domain) dalam array asosiatif ayang nilainya adalah domain dan kuncinya adalah nomor baris. Kemudian, ketika kita sampai ke file berikutnya, itu hanya akan mencetak apa pun yang disimpan di auntuk nomor baris ini, bersama dengan apa yang ada di file 2 untuk nomor baris yang sama.

Perhatikan bahwa ini mengasumsikan kedua file memiliki jumlah baris yang persis sama dan sebenarnya tidak "acak", karena tidak akan membiarkan apa pun diulang. Tapi sepertinya itu yang ingin Anda tanyakan.


5

Solusi Python 2.7 dan 3

Solusi ini menggantikan kemunculan pertama dari string tunggal yang diberikan sewenang-wenang ("jarum") di setiap baris file input dengan string setiap kali dipilih secara acak dari serangkaian garis dari daftar string pengganti.

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

Seharusnya hampir sepele untuk menjangkar jarum ke awal atau akhir string atau menggunakan ekspresi reguler sama sekali.

Pemakaian

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

Contoh:

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

atau

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

3

Berikut cara 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

Solusi bash lain. Ini menggunakan fitur pengganti string bawaan bash. Ini juga mengasumsikan hanya file2.txtberisi string pengganti. Jika tidak, mereka dapat difilter terlebih dahulu menggunakangrep -o <replace> file2.txt

Dengan shuf

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

Tanpa shuf(hampir murni bash)

Di sini kita harus membuat fungsi pertama yang meniru shufseperti itu

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

Maka itu mirip

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

Uji:

$ 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
$ 
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.