Использование head и tail для захвата различных наборов строк и сохранения в одном файле

Использование head и tail для захвата различных наборов строк и сохранения в одном файле

Итак, это домашнее задание, но я не буду задавать конкретный вопрос по нему.

Мне нужно использовать head и tail, чтобы захватить разные наборы строк из одного файла. Например, строки 6-11 и строки 19-24 и сохранить их обе в другом файле. Я знаю, что могу сделать это с помощью append, например

head -11 file|tail -6 > file1; head -24 file| tail -6 >> file1. 

Но я не думаю, что мы должны это делать.
Есть ли какой-то конкретный способ объединить команды head и tail, а затем сохранить в файл?

решение1

Вы можете сделать это с помощью headодной только базовой арифметики, если сгруппируете команды с { ... ; }помощью такой конструкции, как

{ head -n ...; head -n ...; ...; } < input_file > output_file

где все команды используют одни и те же входные данные (спасибо@mikeserv).
Получение строк 6-11 и строк 19-24 эквивалентно:

head -n 5 >/dev/null  # dump the first 5 lines to `/dev/null` then
head -n 6             # print the next 6 lines (i.e. from 6 to 11) then
head -n 7 >/dev/null  # dump the next 7 lines to `/dev/null` ( from 12 to 18)
head -n 6             # then print the next 6 lines (19 up to 24)

Итак, по сути, вы бы запустили:

{ head -n 5 >/dev/null; head -n 6; head -n 7 >/dev/null; head -n 6; } < input_file > output_file

решение2

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

{ head -n 11 file | tail -n 6; head -n 24 file | tail -n 6; } >file1

Вместо того, чтобы дублировать первые M+N строк и сохранять только последние N, вы можете пропустить первые M строк и дублировать следующие N. Этозаметно быстрее на больших файлах. Обратите внимание, что +Nаргумент — tailэто не количество строк, которые нужно пропустить, а единица плюс это — это номер первой строки для печати, причем строки нумеруются с 1.

{ tail -n +6 file | head -n 6; tail -n +19 file | head -n 6; } >file1

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

{ tail -n +6 | head -n 6; tail -n +14 | head -n 6; } <file >file1

В общем случае это не работает. (Возможно, это сработает в некоторых системах, по крайней мере, если входные данные — это обычный файл.) Почему? Из-забуферизация входных данных. Большинство программ, включая tail, не считывают входные данные побайтно, а по несколько килобайт за раз, потому что это быстрее. Поэтому tailсчитывает несколько килобайт, пропускает немного в начале, передает немного больше в headи останавливается — но то, что считано, считывается и недоступно для следующей команды.

Другой подходзаключается в использовании headконвейера/dev/nullпропускать строки.

{ head -n 5 >/dev/null; head -n 6; head -n 7 >/dev/null; head -n 6; } <file >file1

Опять же, это не гарантирует работу из-за буферизации. Это работает с командой headиз GNU coreutils (той, что есть в невстроенных системах Linux), когда ввод из обычного файла. Это потому, что как только эта реализация headпрочитала то, что она хочет, онаустанавливает позицию файлак первому байту, который он не вывел. Это не работает, если вход — это канал.

Более простой способ распечатать несколько последовательностей строк из файла — вызвать более универсальный инструмент, такой какседилиawk. (Это может быть медленнее, но это имеет значение только для очень больших файлов.)

sed -n -e '6,11p' -e '19,24p' <file >file1
sed -e '1,5d' -e '12,18d' -e '24q' <file >file1
awk '6<=NR && NR<=11 || 19<=NR && NR<=24' <file >file1
awk 'NR==6, NR==11; NR==19, NR==24' <file >file1

решение3

Я знаю, вы сказали, что нужно использовать head и tail, но sed — определенно более простой инструмент для этой работы.

$ cat foo
a 1 1
a 2 1
b 1 1
a 3 1
c 3 1
c 3 1
$ sed -ne '2,4p;6p' foo
a 2 1
b 1 1
a 3 1
c 3 1

Вы даже можете собрать блоки в строку с помощью какого-либо другого процесса и запустить его через sed.

$ a="2,4p;6p"
$ sed -ne $a foo
a 2 1
b 1 1
a 3 1
c 3 1

-n отменяет вывод, затем вы указываете диапазоны для печати с помощью p, при этом первое и последнее число диапазона разделяются запятой.

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

$ head -4 foo | tail -3; head -6 foo | tail -1
a 2 1
b 1 1
a 3 1
c 3 1

Чем больше строк в файле и чем больше блоков, тем эффективнее будет работать sed.

решение4

Используйте такую ​​функцию bash:

seq 1 30 > input.txt
f(){ head $1 input.txt | tail $2 >> output.txt ;}; f -11 -2; f -24 -3
cat output.txt
10
11
22
23
24

В данном случае это немного излишне, но если ваши фильтры станут больше, это может стать благом.

Связанный контент