Предположим, у меня есть файл (назовите его sample.txt), который выглядит следующим образом:
Row1,10
Row2,20
Row3,30
Row4,40
Я хочу иметь возможность работать с потоком из этого файла, который по сути является попарной комбинацией всех четырех строк (поэтому мы должны в итоге получить 16). Например, я ищу потоковую (то есть эффективную) команду, где вывод:
Row1,10 Row1,10
Row1,10 Row2,20
Row1,10 Row3,30
Row1,10 Row4,40
Row2,20 Row1,10
Row1,20 Row2,20
...
Row4,40 Row4,40
Мой вариант использования заключается в том, что я хочу передать этот вывод в другую команду (например, awk) для вычисления некоторого показателя об этой попарной комбинации.
У меня есть способ сделать это в awk, но меня беспокоит то, что мое использование блока END {} означает, что я в основном сохраняю весь файл в памяти перед выводом. Пример кода:
awk '{arr[$1]=$1} END{for (a in arr){ for (a2 in arr) { print arr[a] " " arr[a2]}}}' samples/rows.txt
Row3,30 Row3,30
Row3,30 Row4,40
Row3,30 Row1,10
Row3,30 Row2,20
Row4,40 Row3,30
Row4,40 Row4,40
Row4,40 Row1,10
Row4,40 Row2,20
Row1,10 Row3,30
Row1,10 Row4,40
Row1,10 Row1,10
Row1,10 Row2,20
Row2,20 Row3,30
Row2,20 Row4,40
Row2,20 Row1,10
Row2,20 Row2,20
Существует ли эффективный способ потоковой передачи данных без необходимости сохранять файл в памяти и затем выводить его в блок END?
источник
Ответы:
Вот как это сделать в awk, чтобы не хранить весь файл в массиве. Это в основном тот же алгоритм, что и у Тердона.
При желании вы можете даже указать ему несколько имен файлов в командной строке, и он будет обрабатывать каждый файл независимо, объединяя результаты вместе.
В моей системе это занимает примерно 2/3 времени решения perdon от Terdon.
источник
Я не уверен, что это лучше, чем делать это в памяти, но с тем,
sed
чтоr
убирает свой инфил для каждой строки в своем инфиле, а другой - по другую сторону канала, чередуяH
старое пространство с входными строками ...ВЫХОД
Я сделал это по-другому. Он хранит некоторые в памяти - он хранит строку вроде:
... для каждой строки в файле.
Это очень быстро. Это
cat
файл столько раз, сколько строк в файле|pipe
. С другой стороны канала этот вход объединяется с самим файлом столько раз, сколько строк в файле.case
Материал только для портативности -yash
иzsh
как добавить один элемент к расколу, в то время какmksh
иposh
оба проигрывают один.ksh
,dash
,busybox
, Иbash
все отщепляются точно так много полей , так как есть нули , как напечатаноprintf
. Как написано выше, результаты дают одинаковые результаты для каждой из вышеупомянутых оболочек на моей машине.Если файл очень длинный, могут возникнуть
$ARGMAX
проблемы со слишком большим количеством аргументов, и в этом случае вам нужно будет ввестиxargs
или аналогичный.Учитывая тот же вход, который я использовал до того, как выход идентичен. Но если бы я пошел больше ...
Это создает файл, почти идентичный тому, который я использовал ранее (без 'Row') - но в 1000 строк. Вы сами видите, как быстро это происходит:
При 1000 строках есть небольшие различия в производительности между оболочками - они
bash
всегда самые медленные - но поскольку единственная работа, которую они выполняют, это генерирование строки arg (1000 копийfilename -
), эффект минимален. Разница в производительности междуzsh
- как указано выше - иbash
составляет сотую долю секунды здесь.Вот еще одна версия, которая должна работать для файла любой длины:
Он создает мягкую ссылку на свой первый аргумент
/tmp
с полуслучайным именем, чтобы не зацикливаться на странных именах файлов. Это важно, потому чтоcat
арги передаются через каналxargs
.cat
Выходные данные сохраняются в<&3
то время какsed
p
while печатает каждую строку в первом аргументе столько раз, сколько строк в этом файле - и его сценарий также передается в него через канал. Сноваpaste
объединяет свои входные данные, но на этот раз он принимает только два аргумента-
для стандартного ввода и имени ссылки/dev/fd/3
.Последнее -
/dev/fd/[num]
ссылка - должно работать в любой системе linux и многих других, но если оно не создает именованный канал сmkfifo
использованием этого, вместо этого должно работать.Последнее, что он делает, это
rm
это мягкая ссылка, которую он создает перед выходом.Эта версия на самом деле еще быстрее в моей системе. Я полагаю, это потому, что, хотя он исполняет больше приложений, он сразу же начинает передавать им их аргументы - тогда как прежде чем он сложил их все сначала
источник
ctrl+v; ctrl+j
для получения новых строк, как я.. ./file; fn_name
в этом случае.Ну, вы всегда можете сделать это в вашей оболочке:
Это намного медленнее, чем ваше
awk
решение (на моей машине это заняло ~ 11 секунд для 1000 строк, по сравнению с ~ 0,3 секунды вawk
), но, по крайней мере, оно никогда не удерживает в памяти более пары строк.Цикл выше работает для очень простых данных, которые вы имеете в своем примере. Он задохнется от обратной косой черты и съест отставание и пробелы. Более надежная версия того же:
Другой выбор - использовать
perl
вместо:Сценарий выше будет читать каждую строку входного файла (
-ln
), сохранять его как$l
, открыватьsample.txt
снова и печатать каждую строку вместе с$l
. Результатом являются все парные комбинации, в то время как только 2 строки хранятся в памяти. В моей системе это заняло всего около0.6
секунд на 1000 строк.источник
echo
может быть проблемой. То, что я написал (я добавилprintf
сейчас), должно работать со всеми из них правильно? Что касаетсяwhile
цикла, почему? Что не так сwhile read f; do ..; done < file
? Конечно, вы не предлагаетеfor
петлю! Какая другая альтернатива?С
zsh
:$^a
в массиве включается в скобки расширение (как в{elt1,elt2}
) для массива.источник
Вы можете скомпилировать этот код C ++ для довольно быстрых результатов.
Это завершается примерно за 0,19 - 0,27 секунды в файле из 1000 строк.
В настоящее время он считывает
10000
строки в память (для ускорения печати на экран), который, если бы у вас было1000
символов в строке, использовал бы меньше10mb
памяти, что, я не думаю, было бы проблемой. Вы можете полностью удалить этот раздел и просто распечатать его на экране, если это действительно вызывает проблемы.Вы можете скомпилировать, используя
g++ -o "NAME" "NAME.cpp"
Где
NAME
имя файла, чтобы сохранить его иNAME.cpp
файл, в котором этот код сохраненCTEST.cpp:
демонстрация
источник
Поле 2 является пустым и равным для всех элементов в file.txt, поэтому
join
будет объединять каждый элемент со всеми остальными: оно фактически вычисляет декартово произведение.источник
Один из вариантов с Python - сопоставить файл с памятью и воспользоваться тем фактом, что библиотека регулярных выражений Python может работать непосредственно с отображенными в память файлами. Хотя это выглядит как запуск вложенных циклов над файлом, отображение памяти обеспечивает оптимальное использование доступной физической памяти операционной системой.
Альтернативное быстрое решение в Python, хотя эффективность памяти все еще может быть проблемой
источник
В bash ksh также должен работать, используя только встроенные функции оболочки:
Обратите внимание, что, хотя он хранит весь файл в памяти в переменной оболочки, ему нужен только один доступ для чтения к нему.
источник
sed
решение.Объяснение:
sed 'r file2' file1
- прочитать все содержимое файла file2 для каждой строки file1.1~i
означает 1-ю строку, затем 1 + i строку, 1 + 2 * i, 1 + 3 * i и т. Д. Следовательно,1~$((line_num + 1)){h;d}
означаетh
старуюd
указанную линию для буфера, выборочное пространство шаблона и начало нового цикла.'G;s/(.*)\n(.*)/\2 \1/'
- для всех строк, кроме выбранных на предыдущем шаге, выполните следующее:G
et line из буфера для удержания и добавьте его к текущей строке. Затем поменяйте местами строки. Былcurrent_line\nbuffer_line\n
, сталbuffer_line\ncurrent_line\n
Выход
источник