Как я могу повторить содержимое файла n раз?

17

Я пытаюсь сопоставить два разных способа обработки файла. У меня небольшое количество входных данных, но для того, чтобы получить хорошие сравнения, мне нужно повторять тесты несколько раз.

Вместо повторения тестов я хотел бы дублировать входные данные несколько раз (например, 1000), поэтому 3-строчный файл становится 3000 строк, и я могу выполнить гораздо более полноценный тест.

Я передаю входные данные через имя файла:

mycommand input-data.txt
    
задан Oli 08.09.2014 в 12:39
источник

8 ответов

21

Вам не нужен input-duplicated.txt .

Try:

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

Объяснение

  • 0777 : -0 устанавливает набор разделителя входных данных (специальная переменная perl $/ , которая по умолчанию является символом новой строки). Установка этого значения в значение, превышающее 0400 , приведет к тому, что Perl удалит весь входной файл в память.
  • pe : -p означает «распечатать каждую строку ввода после применения скрипта, данного -e к нему».
  • $_=$_ x 1000 : $_ - текущая строка ввода. Поскольку мы читаем весь файл сразу из-за -0700 , это означает весь файл. В x 1000 будет напечатано 1000 копий всего файла.
ответ дан cuonglm 08.09.2014 в 13:36
источник
9

Я изначально думал, что мне придется создавать дополнительный файл, но я мог бы просто закодировать исходный файл в 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 разделит это на столько аргументов, сколько позволит система. Это much быстрее, чем n отдельные кошки.

Способ awk (вдохновленный ответом тердона), вероятно, наиболее оптимизирован, но он дублирует каждую строку на время. Это может быть или не соответствовать конкретному приложению, но оно молниеносно и эффективно.

Но это порождает «на лету». Вывод Bash, вероятно, будет намного медленнее, чем что-то, что можно прочитать, поэтому вы должны создать новый файл для тестирования. К счастью, это только очень простое расширение:

(for _ in {1..1000}; do echo input-data.txt; done) | xargs cat > input-duplicated.txt
mycommand input-duplicated.txt
    
ответ дан Oli 08.09.2014 в 12:39
6

Вот решение awk :

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

Это по существу так же быстро, как и Perl от Gnuc (я побежал и 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
    
ответ дан terdon 08.09.2014 в 15:37
4

Я бы просто использовал текстовый редактор.

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).

    
ответ дан evilsoup 08.09.2014 в 15:07
2

Вот простой однострочный, без скриптов:

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

Объяснение

  • 'yes input-data.txt | head -1000 | paste -s' производит текст input-data.txt 1000 раз, разделенный пробелом.
  • Затем текст передается в cat в виде списка файлов
ответ дан roeeb 10.03.2016 в 19:19
2

Во время работы с совершенно другим сценарием я узнал, что с 29 миллионами строк текста, используя 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() , что не нужно - это просто небольшое удобное для пользователя прикосновение.     

ответ дан Sergiy Kolodyazhnyy 07.01.2017 в 10:40
0

Мы можем решить это без дополнительного файла, а также специальных программ, чистого Bash (ну, cat - стандартная команда).

Основываясь на функции printf внутри bash, мы можем создать повторяющуюся строку):

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 )

Да, двойной & lt; необходимо.

    
ответ дан user379914 30.07.2015 в 08:11
0

Я бы сгенерировал новый файл, используя Unix for loop:

content=$(cat Alex.pgn); for i in {1..900000}; do echo "$content" >> new_file; done 
    
ответ дан SmallChess 12.12.2016 в 04:47