ฉันจะทำซ้ำเนื้อหาของไฟล์ n ครั้งได้อย่างไร


19

ฉันพยายามเปรียบเทียบเพื่อเปรียบเทียบวิธีการประมวลผลไฟล์สองวิธี ฉันมีข้อมูลอินพุตเล็กน้อย แต่เพื่อให้ได้การเปรียบเทียบที่ดีฉันต้องทำการทดสอบซ้ำหลายครั้ง

แทนที่จะทำซ้ำการทดสอบฉันต้องการทำสำเนาข้อมูลอินพุตซ้ำหลายครั้ง (เช่น 1,000) ดังนั้นไฟล์ 3 บรรทัดกลายเป็น 3000 บรรทัดและฉันสามารถรันการทดสอบที่สมบูรณ์มากขึ้น

ฉันกำลังส่งข้อมูลอินพุตผ่านทางชื่อไฟล์:

mycommand input-data.txt

คำตอบ:


21

input-duplicated.txtคุณไม่จำเป็นต้อง

ลอง:

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

คำอธิบาย

  • 0777: -0set ตั้งค่าตัวคั่นเรคคอร์ดอินพุต (ตัวแปรพิเศษ perl $/ซึ่งเป็นบรรทัดใหม่ตามค่าเริ่มต้น) การตั้งค่านี้เป็นค่าที่มากกว่า0400จะทำให้ Perl ใส่ไฟล์อินพุตทั้งหมดลงในหน่วยความจำ
  • pe: -pหมายถึง "พิมพ์แต่ละบรรทัดอินพุตหลังจากใช้สคริปต์ที่กำหนดโดย-eมัน"
  • $_=$_ x 1000: $_เป็นบรรทัดอินพุตปัจจุบัน เนื่องจากเราอ่านไฟล์ทั้งหมดในครั้งเดียวเพราะ-0700นี่หมายความว่าไฟล์ทั้งหมด x 1000จะมีผลใน 1,000 สำเนาของไฟล์ทั้งหมดถูกพิมพ์

ดี นี่มันโง่มาก 0.785s สำหรับ 1,000 xargs, 0.006s สำหรับเรื่องนี้ดังนั้นใช่อาจเอาชนะปัญหาค่าใช้จ่ายที่ฉันเห็นด้วยลูปอื่น ๆ
Oli

และกระแทกที่ถึง 100,000 ครั้งเท่านั้นเพิ่มรันไทม์โดย. 00002 นั่นเป็นเรื่องที่น่าอัศจรรย์มาก
Oli

@Oli: ด้วยไฟล์ขนาดเล็กและคุณมีหน่วยความจำเพียงพอperlมีประสิทธิภาพมากมันออกแบบมาสำหรับสิ่งนี้
cuonglm

11

ตอนแรกฉันคิดว่าฉันจะต้องสร้างไฟล์สำรอง แต่ฉันสามารถวนไฟล์ต้นฉบับใน Bash และใช้การเปลี่ยนเส้นทางบางอย่างเพื่อให้ปรากฏเป็นไฟล์

อาจมีวิธีที่แตกต่างกันหลายสิบวิธีในการทำลูป แต่นี่คือสี่:

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)  #*

วิธีที่สามมีการปรับปรุงจากคอมเม้นของ maru ด้านล่างและสร้างรายการชื่อไฟล์ขนาดใหญ่สำหรับแมว xargsจะแยกสิ่งนี้เป็นข้อโต้แย้งให้มากที่สุดเท่าที่ระบบจะอนุญาต มันมากเร็วกว่าnแมวแยกต่างหาก

awkวิธี (แรงบันดาลใจจากคำตอบของ terdon ) น่าจะเป็นการเพิ่มประสิทธิภาพมากที่สุด แต่มันซ้ำกันในแต่ละบรรทัดในเวลา สิ่งนี้อาจหรืออาจไม่เหมาะกับแอพพลิเคชั่นเฉพาะ แต่มันรวดเร็วและมีประสิทธิภาพ


แต่สิ่งนี้กำลังเกิดขึ้นทันที การส่งออกทุบตีน่าจะช้ากว่าสิ่งที่อ่านได้มากดังนั้นคุณควรสร้างไฟล์ใหม่สำหรับการทดสอบ โชคดีที่เป็นเพียงส่วนขยายที่ง่ายมาก:

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

3
คำสั่งของคุณทั้งคู่มี cat ที่ทำงาน N ครั้ง มันจะมีประสิทธิภาพมากกว่าไหมในการรัน cat หนึ่งครั้งและป้อนหนึ่งอาร์กิวเมนต์ N ครั้ง cat $(for i in {1..N}; do echo filename; done)สิ่งที่ชอบ นี่เป็นข้อ จำกัด ของขนาด arg แต่ควรเร็วกว่า
muru

@muru ความคิดที่ดีเช่นกัน ต้องการงาน แต่ฉันจะเพิ่ม การใช้งานในปัจจุบันกำลังทำซ้ำ 1,000 ไฟล์ใน 7 บรรทัดใน ~ 0.020s นั่นดีกว่ารุ่นของฉันจริง ๆ แต่ไม่ใช่ในระดับ Perl ของ Gnouc
Oli

6

นี่คือawkทางออก:

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

มันเร็วเท่ากับ @ Perl ของ @ Gnuc (ฉันวิ่งทั้ง 1,000 ครั้งและได้เวลาเฉลี่ย):

$ 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
ในความเป็นธรรมคุณอาจลดความซับซ้อนลงไปawk '{for(i=0; i<1000; i++)print}' input-data.txtเพื่อให้มันออกเพียง 1,000 สำเนาของแต่ละบรรทัดในเวลา ไม่เหมาะกับทุกโอกาส แต่เร็วกว่าล่าช้าน้อยกว่าและไม่จำเป็นต้องเก็บไฟล์ทั้งหมดใน RAM
Oli

@Oli แน่นอนฉันคิดว่าคุณต้องการที่จะรักษาคำสั่งซื้อบรรทัดที่123123123ดี แต่111222333ก็ไม่ได้ รุ่นของคุณเร็วกว่า Gnouc ชัดเจนโดยเฉลี่ยอยู่ที่ 0.00297 วินาที แก้ไข: เกาว่าฉันทำผิดมันจริง ๆ แล้วเท่ากับ 0.004013 วินาที
terdon

5

ฉันแค่จะใช้โปรแกรมแก้ไขข้อความ

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)

หากคุณต้องการทำผ่านบรรทัดคำสั่ง (คุณต้องvimติดตั้งเพราะviไม่มี:normalคำสั่ง) คุณสามารถใช้:

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

ที่นี่-es(หรือ-e -s) ทำให้กลุ่มทำงานเงียบ ๆ ดังนั้นจึงไม่ควรใช้ช่วงเวลาปิดเทอร์มินัลของคุณและ-u NONEหยุดไม่ให้ดู vimrc ของคุณซึ่งจะทำให้มันทำงานได้เร็วกว่าที่อื่น (อาจเร็วกว่านี้มากหากคุณใช้ ปลั๊กอินเสียงเรียกเข้าจำนวนมาก)


ใช่ แต่นี่คือคู่มือทั้งหมดซึ่งทำให้คำสั่งหลายขนาดช้าลงและซับซ้อนกว่าโซลูชันอื่น ๆ
terdon

4

นี่คือหนึ่งซับง่ายไม่มีสคริปต์ที่เกี่ยวข้อง:

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

คำอธิบาย

  • `yes input-data.txt | head -1000 | paste -s`สร้างข้อความinput-data.txt1,000 ครั้งคั่นด้วยพื้นที่สีขาว
  • ข้อความจะถูกส่งผ่านไปยังcatรายการไฟล์

วิธีนี้ดูเหมือนจะไม่ทำงาน คุณจำเป็นต้องใช้xargs paste -sหรือไม่? ใช้งานได้ แต่ไม่เก็บบรรทัดใหม่ในไฟล์อินพุต
JeremyKun

ตรวจสอบให้แน่ใจว่าคุณกำลังใช้เครื่องหมายอะโพสโทรฟีที่ถูกต้อง
roeeb

2

ในขณะที่ทำงานกับสคริปต์ที่แตกต่างอย่างสิ้นเชิงฉันได้เรียนรู้ว่าด้วยข้อความ 29 ล้านบรรทัดการใช้seek()และการดำเนินการกับข้อมูล bytewise มักจะเร็วกว่าแบบทีละบรรทัด แนวคิดเดียวกันนี้ถูกนำไปใช้ในสคริปต์ด้านล่าง: เราเปิดไฟล์และแทนที่จะวนซ้ำผ่านการเปิดและปิดไฟล์ (ซึ่งอาจเพิ่มโอเวอร์เฮดแม้ว่าจะไม่มีนัยสำคัญ) เราเปิดไฟล์ไว้และกลับไปสู่จุดเริ่มต้น

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

สคริปต์นั้นใช้งานง่ายมาก:

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

สำหรับไฟล์ข้อความ 3 บรรทัดและ 1,000 การวนซ้ำมันจะไม่เป็นไรประมาณ 0.1 วินาที

$ /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

ตัวสคริปต์ไม่ได้สวยที่สุดอาจจะสั้นลง แต่ทำงานได้ แน่นอนฉันได้เพิ่มบิตพิเศษอีกเล็กน้อยที่นี่เช่นเดียวกับerror_out()ฟังก์ชั่นซึ่งไม่จำเป็น - มันเป็นเพียงการสัมผัสที่ใช้งานง่ายขนาดเล็ก


1

เราสามารถแก้ปัญหานี้ได้โดยไม่ต้องมีไฟล์เพิ่มเติมหรือโปรแกรมพิเศษ pure Bash (ดีแมวเป็นคำสั่งมาตรฐาน)

ขึ้นอยู่กับคุณสมบัติของ printf ภายใน bash เราสามารถสร้างสตริงซ้ำได้):

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

จากนั้นเราสามารถส่งรายชื่อ 1,000 ชื่อ (ซ้ำ) และเรียก cat:

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

และในที่สุดเราสามารถให้ผลลัพธ์กับคำสั่งเพื่อดำเนินการ:

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

หรือหากคำสั่งต้องการรับอินพุตใน stdin:

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

ใช่จำเป็นต้องใช้ double <


0

ฉันจะสร้างไฟล์ใหม่โดยใช้ Unix สำหรับลูป:

content=$(cat Alex.pgn); for i in {1..900000}; do echo "$content" >> new_file; done 
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.