ファイルからランダムにテキストを置き換える方法は?


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
それはランダムではなく、何も繰り返されたくないようです。実際にランダムにしますか、それとも2番目のテキストファイルの各行を1回だけ使用しますか?また、それ bashである必要がありますか、それとも他のツールを利用できますか?
terdon 2017

1
@terdonランダムな順列が必要なようです(5つの要素すべてがランダムな順序で)。ランダムな順列は実際にはランダムです。次の要素をランダムに選択するときは、すでに選択されている要素を削除する必要があります。「ランダムソート」と呼ばれることもあります
thomasrutter 2017

1
@thomasrutterはい、私はそれを知っています。それが私の答えです。しかし、ランダムな順列とランダムなピックの両方が必要なものに応じて合理的であるため、OPに明確にするように依頼したのはそのためです。
terdon 2017

回答:


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

1
いいね!私はそれを行うことを検討していましたpasteが、cut一致しないフィールドを削除するために使用することは思いつきませんでした。
terdon 2017

2
貼り付けソリューションの欠点の1つは、file1にfile2よりも多くの行がある場合です。代わりに、<(sort -R file2.txt)次のようなものを使用できます。これ<(yes "$(<file2.txt)" | head -n $(wc -l < file1.txt) | sort -R)により、ランダム性が歪められ、file2の先頭に近い行が優先されます。
グレン・ジャックマン2017

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そのように使用すると、単語の分割とファイル名の拡張が行われます。
グレン・ジャックマン2017

2
これはfile1.txt、このファイルが空の行で終わっていない場合の最後の空でない行をキャッチしますか(申し訳ありませんが、現時点ではテストできません)?推奨しない場合は、変数に値を割り当てる行ごとにファイルwhile IFS='' read -r orig || [[ -n "$orig" ]]; do読み取る・SOを参照してください。
デザート

2
@janosトピックで非常に良い質問が見つかりました:シェルスクリプトが最後の行がありません
デザート

5

2番目のファイルの行をシャッフル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最初のファイルが読み取られている間のみtrueになります)、2番目のフィールド(フィールドはによって定義される@ため、これがドメインです)を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

これが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
$ 
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.