Использование обоих утилит GNU с Mac Utils в Bash
Я работаю с графикой очень больших файлов с N количеством соответствующих записей данных. (N варьируется между файлами).
В каждом из этих файлов комментарии автоматически генерируются в начале и в конце файла и хотели бы отфильтровать их перед объединением в один большой набор данных.
К сожалению, я использую 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. К сожалению, это займет некоторое время, так как мне нужно написать новый файл с первым заголовком.
Есть ли обходной путь для включения обоих GNU Utils со стандартными Mac Utils?
Спасибо кевен
2 ответа
Проблема с вашей командой заключается в том, что вы снова указываете файловый операнд для ghead
команда, вместо того, чтобы позволить ему принимать свой ввод от стандартного ввода через канал; это вызывает ghead
игнорировать ввод stdin, поэтому первый сегмент трубы фактически игнорируется; просто опустите файловый операнд для ghead
команда:
tail -n+9 "$COUNTER/test.csv" | ghead -n -1 >> gfinal.csv
Тем не менее, если вы хотите удалить только последнюю строку, нет необходимости в GNU head
- собственный BSD OS X sed
Сделаю:
tail -n +9 "$COUNTER/test.csv" | sed '$d' >> gfinal.csv
$
соответствует последней строке, и d
удаляет его (это означает, что он не будет выводиться).
Наконец, как отмечает @ghoti в комментарии, вы можете сделать все это, используя sed
:
sed -n '9,$ {$!p;}' file
вариант -n
говорит sed
производить вывод только по явному запросу; 9,$
соответствует всему от линии 9
через (,
) конец файла (последняя строка, $
), а также {$!p;}
печатает (p
) каждая строка в этом диапазоне, кроме (!
) последний ($
).
Я понимаю, что ваш вопрос об использовании head
а также tail
, но я отвечу так, как если бы вы заинтересовались решением исходной проблемы, а не выясняли, как использовать эти конкретные инструменты для решения проблемы.:)
Один метод с использованием sed
:
sed -e '1,8d;$d' inputfile
На этом уровне простоты GNU sed и BSD sed работают одинаково. Наш сценарий sed говорит:
1,8d
- удалить строки с 1 по 8,$d
- удалить последнюю строку.
Если вы решили сгенерировать подобный скрипт на лету, остерегайтесь цитат; вам придется избежать знака доллара, если вы поставите его в двойные кавычки.
Еще один метод с использованием 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
,