Bagaimana saya bisa mengulangi isi file n kali?


19

Saya mencoba membandingkan untuk membandingkan dua cara berbeda dalam memproses file. Saya memiliki sedikit data input tetapi untuk mendapatkan perbandingan yang baik, saya perlu mengulangi tes beberapa kali.

Daripada hanya mengulang tes, saya ingin menduplikasi data input beberapa kali (misalnya 1000) sehingga file 3 baris menjadi 3000 baris dan saya bisa menjalankan tes yang jauh lebih memuaskan.

Saya mengirimkan data input melalui nama file:

mycommand input-data.txt

Jawaban:


21

Anda tidak perlu input-duplicated.txt.

Mencoba:

mycommand <(perl -0777pe '$_=$_ x 1000' input-data.txt)

Penjelasan

  • 0777: -0set menetapkan pemisah rekaman input (perl variabel khusus $/yang merupakan baris baru secara default). Mengatur ini ke nilai yang lebih besar daripada yang 0400akan menyebabkan Perl menghirup seluruh file input ke dalam memori.
  • pe: -psarana "cetak setiap baris input setelah menerapkan skrip yang diberikan -ekepadanya".
  • $_=$_ x 1000: $_adalah jalur input saat ini. Karena kita membaca seluruh file sekaligus karena -0700, ini berarti seluruh file. Ini x 1000akan menghasilkan 1000 salinan dari seluruh file yang dicetak.

Bagus. Ini sangat cepat. 0,785 untuk 1000 xarg, 0,006 untuk ini, jadi ya, mungkin mengatasi masalah overhead yang saya lihat dengan loop lain.
Oli

Dan menabrak itu hingga 100000 kali hanya meningkatkan runtime sebesar 0,002s. Itu luar biasa.
Oli

@Oli: Dengan file kecil, dan Anda memiliki cukup memori, perlsangat efisien, ini dirancang untuk ini.
cuonglm

11

Saya awalnya berpikir bahwa saya harus membuat file sekunder, tetapi saya hanya bisa mengulang file asli di Bash dan menggunakan beberapa pengalihan untuk membuatnya muncul sebagai file.

Mungkin ada selusin cara berbeda dalam melakukan loop tetapi di sini ada empat:

mycommand <( seq 1000 | xargs -i -- cat input-data.txt )
mycommand <( for _ in {1..1000}; do cat input-data.txt; done )
mycommand <((for _ in {1..1000}; do echo input-data.txt; done) | xargs cat )
mycommand <(awk '{for(i=0; i<1000; i++)print}' input-data.txt)  #*

Metode ketiga ada improvisasi dari komentar maru di bawah ini dan membangun daftar nama file input untuk kucing. xargsakan memecah ini menjadi argumen sebanyak sistem akan memungkinkan. Ini jauh lebih cepat daripada n kucing terpisah.

The awkway (terinspirasi oleh jawaban Terdon ini ) mungkin adalah yang paling optimal tetapi duplikat setiap baris pada suatu waktu. Ini mungkin cocok atau tidak cocok dengan aplikasi tertentu, tetapi kilat cepat dan efisien.


Tapi ini menghasilkan dengan cepat. Bash keluaran cenderung jauh lebih lambat daripada yang bisa dibaca sehingga Anda harus menghasilkan file baru untuk pengujian. Untungnya itu hanya ekstensi yang sangat sederhana:

(for _ in {1..1000}; do echo input-data.txt; done) | xargs cat > input-duplicated.txt
mycommand input-duplicated.txt

3
Kedua perintah Anda memiliki cat menjalankan N kali. Bukankah lebih efisien untuk menjalankan kucing sekali dan memberinya satu argumen N kali? Sesuatu seperti cat $(for i in {1..N}; do echo filename; done). Ini memiliki batasan ukuran arg, tetapi harus lebih cepat.
muru

@uru Ide bagus juga. Butuh beberapa pekerjaan tetapi saya akan menambahkannya. Implementasi saat ini adalah melakukan 1000 iterasi file 7-line dalam ~ 0,020s. Itu benar-benar jauh lebih baik daripada versi saya, tetapi tidak pada tingkat Perl Gnouc.
Oli

6

Inilah awksolusinya:

awk '{a[NR]=$0}END{for (i=0; i<1000; i++){for(k in a){print a[k]}}}' file 

Ini pada dasarnya secepat @ Gnuc's Perl (saya berlari 1000 kali dan mendapatkan waktu rata-rata):

$ for i in {1..1000}; do 
 (time awk '{a[NR]=$0}END{for (i=0;i<1000;i++){for(k in a){print a[k]}}}' file > a) 2>&1 | 
    grep -oP 'real.*?m\K[\d\.]+'; done | awk '{k+=$1}END{print k/1000}'; 
0.00426

$ for i in {1..1000}; do 
  (time perl -0777pe '$_=$_ x 1000' file > a ) 2>&1 | 
    grep -oP 'real.*?m\K[\d\.]+'; done | awk '{k+=$1}END{print k/1000}'; 
0.004076

1
Dalam keadilan, Anda mungkin bisa menyederhanakan ini awk '{for(i=0; i<1000; i++)print}' input-data.txtsehingga hanya mengeluarkan 1000 salinan setiap baris pada suatu waktu. Tidak akan cocok dengan semua kesempatan tetapi lebih cepat, lebih sedikit penundaan dan tidak perlu menyimpan seluruh file dalam RAM.
Oli

@ Oli memang, saya berasumsi Anda ingin menjaga urutan garis jadi 123123123itu baik-baik saja tetapi 111222333tidak. Versi Anda jelas lebih cepat daripada Gnouc, rata-rata pada 0,00297 detik. SUNTING: awal itu, saya membuat kesalahan, itu sebenarnya setara pada 0,004013 detik.
terdon

5

Saya hanya akan menggunakan editor teks.

vi input-data.txt
gg (move cursor to the beginning of the file)
yG (yank til the end of the file)
G (move the cursor to the last line of the file)
999p (paste the yanked text 999 times)
:wq (save the file and exit)

Jika Anda benar-benar perlu melakukannya melalui command-line (ini mengharuskan Anda untuk vimmenginstal, karena vitidak memiliki :normalperintah), Anda dapat menggunakan:

vim -es -u NONE "+normal ggyGG999p" +wq input-data.txt

Di sini, -es(atau -e -s) membuat vim beroperasi secara diam-diam, sehingga tidak boleh mengambil alih jendela terminal Anda, dan -u NONEmenghentikannya dari melihat vimrc Anda, yang seharusnya membuatnya menjalankan sedikit lebih cepat daripada yang seharusnya (mungkin jauh lebih cepat, jika Anda menggunakan banyak plugin vim).


Ya, tetapi ini semua manual yang membuatnya beberapa kali lipat lebih lambat dan lebih kompleks daripada solusi lainnya.
terdon

4

Berikut ini adalah one-liner sederhana, tidak ada skrip yang terlibat:

mycommand <(cat `yes input-data.txt | head -1000 | paste -s`)

Penjelasan

  • `yes input-data.txt | head -1000 | paste -s`menghasilkan teks input-data.txt1000 kali dipisahkan oleh spasi
  • Teks tersebut kemudian diteruskan ke catsebagai daftar file

Solusi ini sepertinya tidak berhasil. Apakah Anda perlu menggunakan xargs paste -s? Ini berfungsi, tetapi tidak mempertahankan baris baru dalam file input.
JeremyKun

Pastikan Anda menggunakan tanda kutip yang benar.
roeeb

2

Saat bekerja pada skrip yang sama sekali berbeda, saya telah belajar bahwa dengan 29 juta baris teks, menggunakan seek()dan mengoperasikan data dengan sendirinya seringkali lebih cepat daripada secara garis demi garis. Gagasan yang sama diterapkan dalam skrip di bawah ini: kita membuka file, dan alih-alih membuka dan menutup file (yang dapat menambah overhead, meskipun tidak signifikan), kita tetap membuka file dan mencari kembali ke awal.

#!/usr/bin/env python3
from __future__ import print_function
import sys,os

def error_out(string):
    sys.stderr.write(string+"\n")
    sys.exit(1)

def read_bytewise(fp):
    data = fp.read(1024)
    print(data.decode(),end="",flush=True)
    while data:
        data = fp.read(1024)
        print(data.decode(),end="",flush=True)
    #fp.seek(0,1)

def main():
    howmany = int(sys.argv[1]) + 1
    if not os.path.isfile(sys.argv[2]):
       error_out("Needs a valid file") 

    fp = open(sys.argv[2],'rb')
    for i in range(1,howmany):
        #print(i)
        fp.seek(0)
        read_bytewise(fp)
    fp.close()

if __name__ == '__main__': main()

Script itu sendiri cukup sederhana dalam penggunaan:

./repeat_text.py <INT> <TEXT.txt>

Untuk file teks 3 baris dan 1000 iterasi berjalan cukup baik, sekitar 0,1 detik:

$ /usr/bin/time ./repeat_text.py 1000 input.txt  > /dev/null                                                             
0.10user 0.00system 0:00.23elapsed 45%CPU (0avgtext+0avgdata 9172maxresident)k
0inputs+0outputs (0major+1033minor)pagefaults 0swaps

Script itu sendiri tidak paling elegan, mungkin bisa disingkat, tetapi berhasil. Tentu saja, saya menambahkan beberapa bit tambahan di sana-sini, seperti error_out()fungsi, yang tidak perlu - itu hanya sentuhan kecil yang ramah pengguna.


1

Kita dapat menyelesaikan ini tanpa file tambahan, atau program khusus, Bash murni (well, cat adalah perintah standar).

Berdasarkan fitur printf di dalam bash, kami dapat membuat string berulang):

printf "test.file.txt %.0s\n" {1..1000}

Kemudian, kami dapat mengirim daftar 1000 nama file (berulang) dan memanggil kucing:

printf "test.file.txt %.0s" {1..1000} | xargs cat 

Dan akhirnya, kita bisa memberikan output pada perintah untuk dieksekusi:

mycommand "$( printf "%.0sinput.txt\n" {1..1000} | xargs cat )"

Atau, jika perintah perlu menerima input di stdin:

mycommand < <( printf "%.0sinput.txt\n" {1..1000} | xargs cat )

Ya, dobel <diperlukan.


0

Saya akan menghasilkan file baru menggunakan Unix for loop:

content=$(cat Alex.pgn); for i in {1..900000}; do echo "$content" >> new_file; done 
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.