4

Предположим, у меня есть 10 МБ текстовый файл foo.txt , и он имеет 100 000 строк. Теперь я хочу обработать 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 ответа4

5

Вы можете сделать это с помощью 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 или здесь.

1

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

Во всяком случае, если вы хотите продолжать использовать 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
0

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

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

$ echo {1..33} | tr \  \\n |\
    awk '{lines=NR} END{ if (lines%10!=0) { print "leftover lines"} }'
leftover lines

# STEP1 use modulo to do something every tenth
$ echo {1..200} |tr \  \\n |\
    awk '{a[NR%10]=$0; if (NR%10==0) {print "ten"} }' | cat -n
     1  ten
     2  ten
     3  ten
     4  ten
     5  ten
     6  ten
     7  ten
     8  ten
     9  ten
    10  ten
    11  ten
    12  ten
    13  ten
    14  ten
    15  ten
    16  ten
    17  ten
    18  ten
    19  ten
    20  ten

# STEP 2 do something with every line
$ echo {1..10} | tr \  \\n | awk '{ b+=$0} END {print b}'
55

# putting it together
$ cat every10.awk
{
        a[NR%10]=$0;
        if (NR%10==0) {
                for (i in a) {
                        printf "%s+", a[i]
                        b+=a[i];
                }
                print "0=" b;
                b=0
        }
}
$ echo {1..200} | tr \  \\n | awk -f every10.awk  | column -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, если операция представляет собой простые арифметические или строковые операции.

0

Не уверен, почему это было перенесено из 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() .

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .