find . -type f -name "*.mp3" -exec dirname {} \; | uniq | wc -l
Простой подход: Используйте ex
для изменения строк и передайте весь буфер (измененный файл) через bc
. Затем выведите модифицированную версию.
printf '%s\n' '%s:.*:&/8640000+719529:' 0a scale=10 . '%!bc' %p 'q!' | ex file.txt
Вывод на ваш образец файла:
735235.0000000000
735235.0000001157
735235.0000002314
735235.0000003472
735235.0000004629
Или сохранить изменения, а не просто распечатать их:
printf '%s\n' '%s:.*:&/8640000+719529:' 0a scale=10 . '%!bc' x | ex file.txt
Чтобы увидеть, какие команды передаются в ex
, запустите команду printf
саму по себе:
$ printf '%s\n' '%s:.*:&/8640000+719529:' 0a scale=10 . '%!bc' %p 'q!'
%s:.*:&/8640000+719529:
0a
scale=10
.
%!bc
%p
q!
Давайте теперь разобьем их на команды ex
. Первая из них довольно сложная, поэтому я специально отформатирую объяснение:
%s:.*:&/8640000+719529:
% - For every line of the buffer (file)
s - Run a substitute command
: - Using ':' as the regex delimiter
.* - Match each entire line
: - and replace with
& - The entire line, followed by
/8640000+719529 - this text
: - End command
0a
означает "добавить текст после строки 0", то есть в начало буфера (файла).
Текст scale=10
- это буквальный текст для добавления.
Текст
в строке сама по себе завершает команду "append".
Команда %!bc
передает содержимое всего буфера в качестве стандартного ввода внешней команде bc
и заменяет весь буфер полученным выводом.
Команда %p
означает печать всего буфера (на стандартный вывод).
q!
означает выход без сохранения изменений.
Если у вас очень, очень большой файл, в десятки миллионов строк, это, очевидно, вызывает проблемы. Я исследовал возможные решения для этого, используя ex
, и есть несколько способов сделать это, но в итоге я отказался от этого подхода в пользу гораздо, гораздо более простого, который по-прежнему использует только POSIX определенные инструменты.
Используйте split
для разбиения вашего файла на фрагменты, затем выполните ранее указанную команду для каждого фрагмента и cat
выведите все вместе:
split -l 1000000 -a 3 file.txt myprefix.
for f in myprefix.???; do
printf '%s\n' '%s:.*:&/8640000+719529:' 0a scale=10 . '%!bc' %p 'q!' |
ex "$f"
done > myoutputfile.txt
rm myprefix.???
Команда split
используется здесь для разбиения file.txt
на фрагменты, каждый длиной в четный миллион строк (остаток, конечно, тоже помещается в файл). Поскольку указано -a 3
, суффикс у кусков будет длиной 3 символа. myprefix.aaa
, myprefix.aab
, etc.
Затем каждый файл может быть обработан ex
по отдельности, без необходимости сохранять изменения, так как мы просто перенаправим вывод всего цикла в myoutputfile.txt
(а затем удалим файлы чанков, для аккуратности).
Выполнение этого в оболочке будет очень медленным.
$ awk '{printf "%.10f\n", (($1/(100*86400))+719529)}' filename
735235.0000000000
735235.0000001157
735235.0000002314
735235.0000003473
735235.0000004630
Как видно из последней записи, результаты округления будут немного другими.
Известно, что у ракушек очень низкая скорость обработки информации.
То, о чем вы просите, можно реализовать в оболочке следующим образом:
#!/bin/bash
while read line; do
bc <<<"scale=10;($line/(100*86400))+719529"
done <datafile
Для обработки 1000 строк требуется около 1,1 секунды.
Вся партия из 8,640 миллионов должна занять около 2 часов 41 минуты.
Кроме того, числовые результаты от bc неправильно округляются.
Пять строк из вашего примера дают такие значения:
735235.0000000000
735235.0000001157
735235.0000002314
735235.0000003472
735235.0000004629
Давайте изменим точность на 20, чтобы увидеть больше цифр:
735235.00000000000000000000
735235.00000011574074074074
735235.00000023148148148148
735235.00000034722222222222
735235.00000046296296296296
Например, третья строка, которая заканчивается на 2314
, неправильно округлена, следующая цифра после 4
- 8
, ее следовало округлить до 5
.
Возможно, мы найдем более быстрое решение с помощью awk. Реализация того, о чем вы просите, в awk будет выглядеть так:
$ awk '{printf ("%.10f\n",($0/(100*86400))+719529)}' datafile
735235.0000000000
735235.0000001157
735235.0000002314
735235.0000003473
735235.0000004630
Для обработки 1000 строк требуется всего 0.006 (6 миллисекунд).
Все 8,64 миллиона строк должны быть обработаны примерно за 50 секунд.
Но awk уже превышает свой диапазон точности. По умолчанию он использует 64-битное представление значений с плавающей точкой. Это представление имеет точность около 15 десятичных цифр. Результаты ваших данных имеют целочисленную часть из 6 цифр, десятичная часть может быть оценена как правильная только до 8-й цифры.
На самом деле, если мы попытаемся увеличить количество цифр:
awk '{printf ("%.20f\n",($0/(100*86400))+719529)}' datafile
мы получим просто шум:
735235.00000000000000000000
735235.00000011571682989597
735235.00000023143365979195
735235.00000034726690500975
735235.00000046298373490572
Сравните с более точными результатами bc:
735235.00000000000000000000
735235.00000000000000000000
735235.00000011571682989597
735235.00000011574074074074
735235.00000023143365979195
735235.00000023148148148148
735235.00000034726690500975
735235.00000034722222222222
735235.00000046298373490572
735235.00000046296296296296
Чтобы действительно решить эту проблему, нам нужен более точный awk.
Если вы используете GNU awk (далее я буду называть его gawk) и он был скомпилирован с MPFR (библиотека многократной точности с плавающей точкой), вы можете получить большую точность.
Убедитесь, что в вашем awk есть эта библиотека (просто спросите ее версию):
$ awk --version
GNU Awk 4.1.3, API: 1.1 (GNU MPFR 3.1.5, GNU MP 6.1.1)
Copyright (C) 1989, 1991-2015 Free Software Foundation.
И измените команду awk, чтобы использовать доступную точность:
gawk -M -v PREC=100 '{printf ("%.20f\n",($0/(100*86400))+719529)}' datafile
735235.00000000000000000000
735235.00000011574074074074
735235.00000023148148148148
735235.00000034722222222222
735235.00000046296296296296
Результаты те же, что и при использовании bc высокой точности.
В этом случае мы получаем скорость awk и точность bc.
Последняя команда для 10 десятичных цифр, которые вы просите, такова:
gawk -M -v PREC=100 '{printf ("%.10f\n",($0/(100*86400))+719529)}' datafile
735235.0000000000
735235.0000001157
735235.0000002315
735235.0000003472
735235.0000004630
Все значения правильно округлены.