2015-11-12 2 views
1

Я работаю с построением чрезвычайно больших файлов с N количеством соответствующих записей данных. (N зависит от файлов).Использование GNU Utils с Mac Utils в bash

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

К сожалению, я использую MacOSx, где возникают некоторые проблемы при попытке удалить последнюю строку файла. Я прочитал, что наиболее эффективным способом было использование команд head/tail bash для отсечения разделов данных. Поскольку head -n -1 не работает для MacOSx, мне пришлось установить coreutils через homebrew, где команда ghead работает чудесно. Однако команда,

tail -n+9 $COUNTER/test.csv | ghead -n -1 $COUNTER/test.csv >> gfinal.csv 

не работает. Менее приятное обходное решение заключалось в том, что мне пришлось отделить команды, использовать ghead> newfile, а затем использовать tail в newfile> gfinal. К сожалению, это займет время, поскольку я должен написать новый файл с первым ghead.

Есть ли способ обхода обоих GNU Utils со стандартными Mac Utils?

Спасибо, Keven

ответ

4

Проблема с командой, что вы указываете файл операнд снова для команды ghead, вместо того, чтобы позволить ему принимать входные данные от стандартного ввода через трубу; это вызывает ghead до игнорировать вход stdin, поэтому t его первый сегмент трубы фактически игнорируется; просто опустить файл операнд для команды ghead:

tail -n+9 "$COUNTER/test.csv" | ghead -n -1 >> gfinal.csv 

Тем не менее, если вы хотите отказаться от последней линии, нет никакой необходимости в GNUhead - собственный BSD OS Х sed будет делать:

tail -n +9 "$COUNTER/test.csv" | sed '$d' >> gfinal.csv 

$ соответствует последней строке, и d удаляет его (то есть он не будет б e).

Наконец, как @ghoti указывает на комментарий, вы могли бы сделать это все с помощью sed:

sed -n '9,$ {$!p;}' file 

Опция -n говорит sed только производить выход при явном запросе; 9,$ соответствует все от линии 9 через (,) конец файла (последняя строку, $) и {$!p;} отпечаток (p) каждую строку в этом диапазоне, за исключением (!) последнего ($).

+0

@ghoti: Отличная точка, спасибо; Я обновил ответ, хотя я выбрал вариант, который, на мой взгляд, лучше выражает намерение. – mklement0

+1

Ах, я удалил свой комментарий, чтобы развернуть его в ответ. :) Ваш новый сценарий sed выражает цель OP с большей поэзией, но я не думаю, что он делает это более четко. Таким образом, он говорит «строки печати, которые соответствуют этим критериям», а не просто «удаляет эти диапазоны строк из потока». Я бы предположил, что это просто разные взгляды на проблему. (Но +1 для вашего великого объяснения, как обычно.) – ghoti

+0

@ghoti: Спасибо; точка зрения изменилась. Я скажу, однако, что моя команда 'sed' более похожа на подход _OP_. – mklement0

2

Я понимаю, что ваш вопрос об использовании head и tail, но я отвечу так, как будто вы заинтересованы в решении исходной проблемы, а не в определении того, как использовать эти конкретные инструменты для решения проблемы.:)

Один метод, использующий sed:

sed -e '1,8d;$d' inputfile 

На этом уровне простоты, ГНУ СЭД и BSD СЭД оба работают таким же образом. Наш СЭД сценарий говорит:

  • 1,8d - удалить строки 1 по 8,
  • $d - удалить последнюю строку.

Если вы решили сгенерировать сценарий sed как этот «на лету», остерегайтесь вашего цитирования; вам придется избегать знака доллара, если вы поместите его в двойные кавычки.

Другой метод, использующий awk:

awk 'NR>9{print last} NR>1{last=$0}' inputfile 

Это работает немного по-другому, чтобы «признать» последнюю строку, захватив предыдущую строку и печать после линии 8, а затем не печатает последнюю строку.

Это awk-решение немного взломано и, как и решение sed, полагается на то, что вы хотите удалить только одну конечную строку файла.

Если вы хотите вырезать больше строк, чем одну в нижней части файла, вы, вероятно, захотите сохранить массив, который будет функционировать как буферный FIFO или скользящее окно.

awk -v striptop=8 -v stripbottom=3 ' 
    { last[NR]=$0; } 
    NR > striptop*2 { print last[NR-striptop]; } 
    { delete last[NR-striptop]; } 
    END { for(r in last){if(r<NR-stripbottom+1) print last[r];} } 
' inputfile 

Вы указываете, сколько полосок в переменных. Массив last хранит несколько строк в памяти, печатает из дальнего конца стека и удаляет их по мере их печати. Раздел END выполняет все, что остается в массиве, и печатает все, что запрещено в stripbottom.

+0

Хорошая точка повторного двойного цитирования; ваша команда 'awk' должна сказать' NR> 9' и может быть оптимизирована путем замены 'NR> 1' на' NR> = 9' или, более широко: 'n = 9; awk "NR> $ n {print last} NR> = $ n {last = \ $ 0}" inputfile' - но, как вы заявляете, это немного взломано. – mklement0

+1

Спасибо, исправил awk one-liner, и да, это была бы оптимизация. Повторяю ваш общий подход, несмотря на другие соображения, я не думаю, что когда-либо использовал двойные кавычки, чтобы содержать awk-скрипт - я боюсь переменных расширений внутри подобных скриптов. Я был бы более склонен: 'awk -v n =" $ n "'NR> n {print last} ...'. – ghoti

+0

Да, хорошая передача переменной переменной - использование '-v' - это путь. Я просто взял ярлык в этом простом случае. – mklement0