2016-12-28 2 views
3

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

find /dir/ -type f | awk -F"|" '$2 ~ /string/{ print $3"|"$7 }' > the_good_stuff.txt

Это никогда не будет масштабироваться так мы вводим xargs.

find /dir/ -type f -print0 | xargs -0 -n1 -P6 awk -F"|" '$2 ~ /string/{ print $3"|"$7 }'

Это дает правильный выход независимо от того, сколько времени мы проводим его. Sweet, поэтому давайте напишем его в файл, добавив > the_good_stuff_from_xargs.txt к этой команде. Кроме того, файл содержит искаженные строки.

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

Я попытался добавить команду со следующим.

> myfile.txt

>> myfile.txt

| mawk '{print $0}' > myfile.txt

И другие различные концепции перенаправлять или иначе «объединение» выход из xargs перед записью на диск с данными, которые повреждены в каждой версии.

Я уверен, что исходные файлы не искажены. Я уверен, что при просмотре в терминале в качестве stdout команда с xargs производит допустимый выход в течение 10 минут, глядя на него текст с запятой ...

Локальный диск - это SSD ... Я читаю и пишу из той же файловой системы.

Почему перенаправление вывода find /dir/ -type f -print0 | xargs -0 -n1 -P6 awk -F"|" '$2 ~ /string/{ print $3"|"$7 }' приводит к искажению данных?

EDIT

Я не могу в настоящее время установить unbuffer но stdbuf -oL -eL изменяет выход команды будет строка в буфер и поэтому, теоретически, должен сделать то же самое.

Я пробовал оба: stdbuf xargs cmd и xargs stdbuf cmd оба привели к чрезвычайно ломающимся линиям.

-P6 требуется для выполнения этой команды в любое разумное время.

EDIT 2

Для уточнения ... xargs и это -P6 флаг требования, чтобы решить эту проблему, так как каталог мы работаем в миллионы файлов, которые необходимо проверить.

Очевидно, что мы могли бы удалить -P6 или каким-либо другим способом остановить запуск нескольких рабочих мест сразу, но это на самом деле не отвечая на вопрос почему выход становится все подогнаны и не является реалистичным подходом к как выход может быть восстанавливается до состояния «правильное», но при этом выполняет задачу по шкале.

Решение

Принятый ответ упоминал об использовании parallel, который работал лучший из всех ответов.

Последняя команда, с которой я работал, выглядела. time find -L /dir/ -type f -mtime -30 -print0 | parallel -0 -X awk -f manual.awk > the_good_stuff.txt Awk было трудно, поэтому я переместил -F"|" в саму команду. По умолчанию параллельная развертка задания на ядро ​​на коробке, вы можете использовать -j, чтобы установить количество заданий ниже, если это необходимо.

В действительно научных терминах это было массовое увеличение скорости. То, что заняло неизмеримое количество часов (вероятно, 6+), составляет 10%, завершено через 6-6 минут, поэтому, скорее всего, закончится в течение часа.

Один из них заключается в том, что вы должны убедиться, что команда, запущенная в parallel, не пытается записать в файл ..., что эффективно обходит обработку вывода, выполняемую параллельно при выполнении заданий!

Истекает без -X Параллельные действия аналогичны xargs -n1.

+2

Стандартный вывод буферизируется по строке при записи на терминал, но он полностью буферизуется при записи в канал или файл. – Barmar

+0

Используйте команду 'unbuffer', которая поставляется с' Expect'. – Barmar

+5

Удалите '-P6'; что приводит к тому, что 6 асинхронных процессов записывают в произвольном порядке на ваш вывод, и они пишут частичные строки по мере заполнения буфера, а разные процессы пишут разные частичные линии в разных точках и т. д. Если вы должны использовать '-P6', вам нужно иметь 6 процессов, записывающих разные файлы, чтобы они не топтались друг от друга. Это, в свою очередь, может означать запуск сценария оболочки, который запускает 'awk' и перенаправляет I/O в отдельный файл (возможно, используйте' mktemp', чтобы указать имя в PID скрипта). –

ответ

2

man xargs упоминает об этой проблеме: «Обратите внимание, что обработанные процессы должны правильно управлять параллельным доступом к общим ресурсам. Например, если более одного из них пытается печатать на stdout, то ouptut будет создан в неопределенная порядок (и весьма вероятно, перепутал)»

к счастью, есть способ, чтобы сделать эту операцию на порядок быстрее и решить проблему коверкая в то же время:

find /dir/ -type f -print0 | xargs -0 awk -F"|" '$2 ~ /string/{ print $3"|"$7 }' 

почему?

-P6 перетасовывает ваш выход, поэтому не используйте его. xargs -n1 запускает один awk процесс для каждого файла, в то время как без n1, xargs запускает много меньше awk процессов, например:

files | xargs -n1 awk 
=> 
awk file1 
awk file2 
... 
awk fileN 

vs 

files | xargs awk 
=> 
awk file1 file2 ... fileN # or broken into a few awk commands if many files 

я побежал ваш код на ~ 20к текстовых файлов каждый ~ 20k в размере и без -n1 -P6:

with -n1 -P6 23.138s 
without  3.356s 

если вы хотите параллелизм без перетасовки стандартного вывода xargs «s, используйте гну parallel (также предложенный Гордон Дэвиссон), например,:

find /dir/ -type f -print0 | parallel --xargs -0 -q awk -F"|" '$2 ~ /string/{ print $3"|"$7 }' 

примечание: -q необходимо процитировать командную строку, в противном случае кавычки в -F"|" и вокруг awk кода становятся неупомянута когда parallel запускает их.

parallel экономит немного времени, но не столько, сколько канав -n1 сделал:

parallel  1.704s 

пс: введение cat (который Мэтт делает в своем ответе) крошечное быстрее, чем просто xargs awk:

xargs awk  3.356s 
xargs cat | awk 3.036s 
+1

Хм я должен был спрятать '-n1', я думаю, что это было сделано, чтобы попытаться уничтожить данные. Я не возражаю, если порядок вывода смешанный. Я имею в виду, когда вывод «поврежден», как и в, половина строки записывается, а другая половина записывается на другую строку ... В любом случае я попытаюсь ваши предложения и сообщит результаты. –

0

я бы просто сделать следующее:

cat /${dir}/* | awk '$2 ~ /string*/{ print $3 "|" $7 }' >> `date`.txt 

Если файл с именем после даты и времени, в котором был запущен процесс.

+1

Я мог ошибаться, но будет ли это нарушено, если есть каталог внутри $ {dir}? Как и в OP, использование «find -f» обычно является хорошим способом получить только файлы. Он даже найдет их рекурсивно, какой кот и шаблон глобуса не будут делать. – diametralpitch

+1

Этот ответ игнорирует требование, что мы пытаемся запустить несколько команд awk, чтобы увеличить скорость выбора «строки» из файлов. –

+0

Он не опускается в подкаталоги (которые не запрашивались). Мы пренебрегаем необходимостью выполнять поиск типа -f таким образом. – Matt