Как эффективнее разрезать файл на куски?

Предположим, у меня есть текстовый файл 10 МБ foo.txtи имеет 100000 строк. Теперь я хочу обработать foo.txt окно за окном, с размером окна 10.

Мой текущий скрипт выглядит так:

for ((i=0;i<$lines;i=i+$step))
do    
    head -$((i+step)) $1 | tail -$step > tmp1
    head -$((i+step)) $2 | tail -$step > tmp2
    setstr=$setstr' '`./accuracy.sh tmp1 tmp2`
done
echo $setstr | awk '{for (i=1;i<=NF;i++) sum+=$i; }END{print sum/NF}'

Но это работает медленно. Есть ли простой и более эффективный способ сделать это?

4 ответа

Вы можете сделать это с split:

Вот пример того, как его использовать:

split -l 10 input_file output_file_prefix_

-l вариант выступает за --lines=

И это расколется input_file в куски по 10 строк в эти файлы:

output_file_prefix_aa
output_file_prefix_ab
output_file_prefix_ac
...

и так далее.

Для других способов вы можете использовать split, увидеть man split или здесь

Было бы полезно иметь немного больше контекста относительно вашей конечной цели, а не фрагмент кода. В частности, есть ли у вас контроль над precision.sh?

Во всяком случае, если вы хотите продолжать использовать Bash, то вы можете сделать

for ((i=0;i<$lines;i+=$step))
do
  let end=i+10
  sed -n $i,${end}p $1 >tmp1
  sed -n $i,${end}p $2 >tmp2
  ...
done

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

Очевидная ошибка, когда входной файл (# строк) не делится на десять. Решение состоит в том, чтобы сделать проверки в предложении END{}. Что-то вроде:

$ echo {1..33} | tr \ \\ n | \
    awk '{lines = NR} END {if (lines% 10! = 0) {вывести "оставшиеся строки"} }'
оставшиеся линии

# ШАГ1 использовать по модулю, чтобы сделать что-то каждую десятую
$ echo {1..200} |tr \  \\n |\
    awk '{a[NR%10]=$0; if (NR%10==0) {print "ten"} }' | кот-н
     1 десять
     2 десять
     3 десять
     4 десять
     5 десять
     6 десять
     7 десять
     8 десять
     9 десять
    10 десять
    11 десять
    12 десять
    13 десять
    14 десять
    15 десять
    16 десять
    17 десять
    18 десять
    19 десять
    20 десять

# ШАГ 2 сделать что-нибудь с каждой строкой
$ echo {1..10} | tr \ \\ n | awk '{b += $ 0} END {print b}'
55

# положить его вместе
$ cat every10.awk
{
        а [NR% 10] = $ 0;
        если (NR% 10 == 0) {
                для (я в) {
                        printf "%s+", a[i]
                        Ь += а [I];
                }
                печать "0=" b;
                б =0
        }
}
$ echo {1..200} | tr \  \\n | awk -f every10.awk  | столбец -s= -t
4+5+6+7+8+9+10+1+2+3+0                     55
14+15+16+17+18+19+20+11+12+13+0            155
24+25+26+27+28+29+30+21+22+23+0            255
34+35+36+37+38+39+40+31+32+33+0            355
44+45+46+47+48+49+50+41+42+43+0            455
54+55+56+57+58+59+60+51+52+53+0            555
64+65+66+67+68+69+70+61+62+63+0            655
74+75+76+77+78+79+80+71+72+73+0            755
84+85+86+87+88+89+90+81+82+83+0            855
94+95+96+97+98+99+100+91+92+93+0           955
104+105+106+107+108+109+110+101+102+103+0  1055
114+115+116+117+118+119+120+111+112+113+0  1155
124+125+126+127+128+129+130+121+122+123+0  1255
134+135+136+137+138+139+140+131+132+133+0  1355
144+145+146+147+148+149+150+141+142+143+0  1455
154+155+156+157+158+159+160+151+152+153+0  1555
164+165+166+167+168+169+170+161+162+163+0  1655
174+175+176+177+178+179+180+171+172+173+0  1755
184+185+186+187+188+189+190+181+182+183+0  1855
194+195+196+197+198+199+200+191+192+193+0  1955

Идея здесь состоит в том, чтобы использовать awk для печати блоков из десяти строк и обрабатывать их, или обрабатывать непосредственно с помощью awk, если операция является простой арифметической или строковой операцией.

Не уверен, почему это было перенесено из StackOverflow. В то время как split ответ в стиле суперпользователя, вопрос был о программировании. Например, вот ответ, который реализует то, что вы ищете в awk,

Один из действительно удобных аспектов awk это то, как он обрабатывает трубы.

#!/usr/bin/awk -f

BEGIN {
  cmd="/path/to/handler"
}

{
  print | cmd
}

NR % 10 == 0 {
  close(cmd)
}

Ваш cmd будет вновь открыт, если он будет закрыт... и будет закрываться через каждую 10-ю строку, чтобы открывать следующую строку вывода.

Эффект будет запускаться handler каждые 10 строк ввода. В конце файла handler будет выполняться с оставшимися строками, как cmd автоматически закрывается при выходе из awk.

Строго говоря, вам не нужно использовать переменную вроде cmd чтобы сохранить команду... но она упрощает настройку команды, так как в противном случае вам нужно было бы ОЧЕНЬ внимательно следить за опечатками в вашем close(),

Другие вопросы по тегам