Grep предпоследняя строка

#bash #grep

#bash #grep

Вопрос:

Как следует из названия, как я могу отфильтровать с помощью grep (или аналогичного инструмента bash) строку перед последней строкой файла (переменной длины)?

То есть показывать все, КРОМЕ предпоследней строки.

Спасибо

Комментарии:

1. Почему закрытое голосование? Это действительный вопрос для начинающих программистов.

Ответ №1:

Вы можете использовать комбинацию head и tail , например, так:

 $ cat input 
one
two
three
HIDDEN
four
$ head -n -2 input ; tail -n 1 input 
one
two
three
four
  

Из документации coreutils head:

‘-n k’
‘—lines=k’
Вывод первых k строк. Однако, если k начинается с ‘-’, выведите все k строк каждого файла, кроме последних. Суффиксы множителя размера такие же, как и в опции -c.

Таким head -n -2 образом, часть удаляет все входные данные, кроме последних двух строк.

К сожалению, это не переносимо. (POSIX не допускает отрицательных значений в -n параметре.)

Комментарии:

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

2. Попробуйте. Это работает, даже с файлами, которые содержат только одну или две строки.

3. Или, может быть, я неправильно понял вопрос. Это не сработает, если входные данные представляют собой не обычные файлы, а поток каналов.

4. Я думаю, что OP допустил здесь ту же ошибку, что и я, прочитав 2, потому что в вашем примере "head -n 2" также было бы достаточно. Прояснил это.

5. 1. Возможно, упомяните, что head -n -2 будет напечатано все, кроме последних двух строк. Не уверен, насколько она переносима, например, для BSD head .

Ответ №2:

grep это неправильный инструмент для этого. Вы можете использовать что-то вроде

 # Get line count
count=$(wc -l <file)
# Subtract one
penultimate=$(expr $count - 1)
# Delete that line, i.e. print all other lines.
# This doesn't modify the file, just prints
# the requested lines to standard output.
sed "${penultimate}d" file
  

Bash имеет встроенные арифметические операторы, которые более элегантны, чем expr ; но expr переносимы на другие оболочки.

Вы также можете сделать это в чистом sed виде, но я не хочу об этом думать. В Perl или awk было бы легко напечатать предыдущую строку, а затем в EOF напечатать последнюю строку.

Редактировать: я sed все-таки подумал.

 sed -n '$!x;1!p' file
  

Более подробно; если мы не находимся на последней строке ( $ ), замените пространство шаблона и пространство удержания (запомните текущую строку; извлеките предыдущую строку, если таковая имеется). Затем, если это не первая строка, выведите все, что сейчас находится в пространстве шаблонов (предыдущую строку, за исключением случаев, когда мы находимся на последней строке).

Комментарии:

1. @tripleee, 1 для версии sed 🙂

Ответ №3:

awk oneliner: (тест с seq 10):

 kent$  seq 10|awk '{a[NR]=$0}END{for(i=1;i<=NR;i  )if(i!=NR-1)print a[i]}'  
1
2
3
4
5
6
7
8
10
  

Ответ №4:

Использование ed :

 printf '%sn' H '$-1d' wq | ed -s file          # in-place file edit
printf '%sn' H '$-1d' ',p'  wq | ed -s file    # write to stdout