如何重复文件内容n次?


19

我正在尝试进行基准测试,以比较处理文件的两种不同方式。我输入的数据很少,但是为了获得良好的比较,我需要重复测试多次。

我想重复输入数据多次(例如1000次),而不是重复测试,因此3行文件变成3000行,并且我可以运行更令人满意的测试。

我通过文件名传递输入数据:

mycommand input-data.txt

Answers:


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将导致打印整个文件的1000份副本。

真好 这太快了。0.785s for 1000 xargs,0.006s为此,是的,可能克服了我在其他循环中看到的开销问题。
奥利(Oli)

而将其提高到100000次只会将运行时间增加0.002s。太神奇了
奥利(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的注释即兴创作了第三个方法,它为cat建立了一个很大的输入文件名列表。xargs会将其拆分为系统允许的尽可能多的参数。这是多大的速度比ñ独立的猫。

awk办法(灵感terdon的答案)可能是最优化的,但它一次复制每一行。这可能适合或不适合特定的应用,但是它的闪电般快速高效。


但是,这是动态产生的。Bash的输出可能比读取的东西要慢得多,因此您应该生成一个新文件进行测试。幸运的是,这只是一个非常简单的扩展:

(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也是个好主意。需要一些工作,但我会添加它。当前的实现是在约0.020秒内对7行文件进行1000次迭代。这确实比我的版本好得多,但在Gnouc的Perl级别上却没有。
奥利(Oli)

6

这是一个awk解决方案:

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

它基本上和@Gnuc的Perl一样快(我都运行了1000次,并获得了平均时间):

$ 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以便一次只发布每行1000份。不适用于所有情况,但速度更快,延迟更短,并且不需要将整个文件保存在RAM中。
奥利(Oli)

@Oli确实,我以为您想保留行顺序,这样123123123很好,但事实111222333并非如此。您的版本显然比Gnouc的版本快,平均为0.00297秒。编辑:从头开始,我犯了一个错误,它实际上等效于0.004013秒。
terdon 2014年

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)使vim静默运行,因此它不应该接管您的终端窗口,并-u NONE阻止它查看您的vimrc,这会使它运行的速度比其他情况要快一些(如果使用,则可能要快得多)很多vim插件)。


是的,但这都是手动操作,这使其比其他解决方案慢了几个数量级,并且变得更加复杂。
terdon 2014年

4

这是一个简单的单行代码,不涉及脚本:

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

说明

  • `yes input-data.txt | head -1000 | paste -s`产生input-data.txt由空格分隔的1000倍的文本
  • 然后将文本cat作为文件列表传递给

该解决方案似乎不起作用。需要使用xargs paste -s吗?这可行,但是不会在输入文件中保留换行符。
JeremyKun

确保使用正确的撇号。
roeeb

2

在处理完全不同的脚本时,我了解到,使用2900万行文本,seek()按字节使用和操作数据通常比逐行操作更快。在下面的脚本中应用了相同的想法:我们打开文件,而不是循环打开和关闭文件(这可能会增加开销,即使不很重要),而是保持文件打开并重新开始。

#!/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行文本文件和1000次迭代,它可以正常运行,大约需要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

我们可以解决此问题,而无需其他文件,也不需要特殊的程序,纯Bash(好吧,cat是标准命令)。

根据bash中printf的功能,我们可以生成一个重复的字符串):

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

然后,我们可以发送1000个文件名的列表(重复)并调用cat:

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

最后,我们可以将输出提供给命令以执行:

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

或者,如果命令需要在标准输入中接收输入:

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

是的,需要double <。


0

我将使用Unix for loop生成一个新文件:

content=$(cat Alex.pgn); for i in {1..900000}; do echo "$content" >> new_file; done 
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.