파일의 내용을 n 번 반복하려면 어떻게해야합니까?


19

파일을 처리하는 두 가지 방법을 비교하기 위해 벤치마킹하려고합니다. 적은 양의 입력 데이터가 있지만 좋은 비교를 위해서는 테스트를 여러 번 반복해야합니다.

테스트를 반복하는 대신 입력 데이터를 여러 번 복제 (예 : 1000)하여 3 줄 파일이 3000 줄이되어 훨씬 더 만족스러운 테스트를 실행할 수 있습니다.

파일 이름을 통해 입력 데이터를 전달합니다.

mycommand input-data.txt

답변:


21

당신은 필요하지 않습니다 input-duplicated.txt.

시험:

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

설명

  • 0777: -0입력 레코드 구분 기호를 설정합니다 ( $/기본적으로 줄 바꿈 인 perl 특수 변수 ). 이 값을 더 큰 값으로 설정 0400하면 Perl이 전체 ​​입력 파일을 메모리에 넣습니다.
  • pe: -p"각 스크립트를 적용한 후 각 입력 행을 인쇄합니다" 라는 의미 -e입니다.
  • $_=$_ x 1000: $_는 현재 입력 라인입니다. 로 인해 전체 파일을 한 번에 읽고 있기 때문에 전체 파일을 -0700의미합니다. 는 x 1000전체 파일의 1,000 카피 인쇄 당할 것이다.

좋은. 이것은 어리 석다. 1000 xargs의 경우 0.785s, 이것의 경우 0.006s이므로 다른 루프에서 보았던 오버 헤드 문제를 극복했을 것입니다.
Oli

그리고이를 100000 배로 늘리면 런타임은 .002 초만 증가합니다. 꽤 놀랍습니다.
Oli

@Oli : 작은 파일로 충분한 메모리를 가지고 있고 perl매우 효율적이며이를 위해 설계되었습니다.
cuonglm

11

원래 보조 파일을 생성해야한다고 생각했지만 Bash에서 원본 파일을 반복하고 리디렉션을 사용하여 파일로 표시 할 수 있습니다.

루프를 수행하는 방법에는 12 가지가 있지만 여기에는 4 가지가 있습니다.

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이것을 시스템이 허용하는 한 많은 인수로 나눕니다. 그것은이다 훨씬 보다 빠른 n은 별도의 고양이.

awk(영감 방법 terdon의 대답은 ) 아마도 가장 최적화 된이지만 한 번에 각 라인을 복제합니다. 이것은 특정 응용 프로그램에 적합하거나 적합하지 않을 수 있지만 번개처럼 빠르고 효율적입니다.


그러나 이것은 즉시 생성됩니다. Bash 출력은 읽을 수있는 것보다 훨씬 느리므로 테스트를 위해 새 파일을 생성해야합니다. 고맙게도 그것은 매우 간단한 확장 일뿐입니다.

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

3
두 명령 모두 고양이가 N 번 실행됩니다. 고양이를 한 번 실행하고 한 번의 인수로 N 번 먹이는 것이 더 효율적이지 않습니까? 같은 것 cat $(for i in {1..N}; do echo filename; done). 이것은 arg 크기의 한계가 있지만 더 빠릅니다.
muru

@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

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 NONEvimrc를 보지 않아야합니다. 많은 vim 플러그인).


예, 그러나 이것은 모두 수동이므로 다른 솔루션보다 몇 배 더 느리고 복잡합니다.
terdon

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

완전히 다른 스크립트로 작업하는 동안 2,900 만 줄의 텍스트를 사용하면 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 내부의 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 )"

또는 명령이 stdin에서 입력을 받아야하는 경우 :

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

예, 이중 <가 필요합니다.


0

유닉스 for 루프를 사용하여 새 파일을 생성합니다.

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.