#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
будет напечатано все, кроме последних двух строк. Не уверен, насколько она переносима, например, для BSDhead
.
Ответ №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