파일의 내용을 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.