tail -n1 в последней непустой строке

Я хотел добиться чего-то подобного

grep -v '^$' <myfile-with-blank-lines-at-the-end> | tail -n 1

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

1 ответ

Решение

Цель состоит в том, чтобы использовать только tail.

Выхода нет.


Анализ

Два используемых вами инструмента были созданы с учетом философии Unix:

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

В вашем примере они работают вместе, передавая текстовый поток друг другу. Каждый делает свое "одно дело":

Эти вещи ортогональны; нет общего компонента.

tailне заботится о содержимом, в частности, его не волнует, пуста ли какая-либо строка. По сути, вы не можете делать то, что хотите, с подошвой. tail. Если кто расширил tail Чтобы справиться с вашим делом, я бы сказал, что это Неправильно.

grepне волнует, дошла ли она до последней строки. Здесь \Zпривязка, предназначенная для сопоставления "в конце строки, а также до последнего разрыва строки в строке (если есть)" или даже "в конце строки, а также до всех разрывов конечной строки в строке (если есть)" (в зависимости от аромата см. это). Последнее было бы полезно в вашем случае, но grep похоже не поддерживает \Z (или что-то подобное) вообще.


Более общие инструменты

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

Действительно, каждая из следующих команд выполняет (почти) то, что делает ваша исходная команда:

sed -n '/./ h; $ {g;p}'
awk '{if ($0 != "") buffer=$0} END {print buffer}'

Почти, потому что, если нет непустой строки (что также включает случай полностью пустого ввода), на выходе будет одна пустая строка. Ваша исходная команда в таком случае ничего не дает. Нам нужна логика, чтобы действительно имитировать вашу команду. Как это:

sed -n '/./ h; $ {g;s/^$//;t;g;p}'
awk '{if ($0 != "") buffer=$0} END {if (buffer != "") print buffer}'

Даже без этой дополнительной логики я считаю оба решения менее краткими, чем ваш grep … | tail …. Здесь нет ничего удивительного. Очень простые сценарии (или / и параметры по умолчанию) для общего инструмента, вероятно, не сделают ничего интересного; в то время как хороший специализированный инструмент предназначен для решения наиболее распространенных задач с довольно простым синтаксисом.

Это означает, что вам не следует ожидать, что вы найдете один общий инструмент, который позволит вам делать то, что вы хотите, так же легко, как grep … | tail …. Если конвейер был длинным и сложным, то sed или же awk может быть лучшим подходом, упрощающим. Но ваш вариант использования - "поиск строк (не) соответствующих шаблону и выбор последней". Это именно то, что grep и tail для.


ПОЦЕЛУЙ

Сделайте это простым и понятным. Ваша исходная команда очень проста. Чтобы примерно понять, что происходит, достаточно лишь некоторых базовых знаний об этих двух инструментах. я вижу grep … | tail …и сразу могу сказать, что мы ищем какой-то шаблон и выберем несколько последних строк. Потому что это то, что делают эти инструменты соответственно.

я вижу awk … или же sed …и это может быть что угодно. Даже если какой-то (эзотерический? Игра в гольф?) Язык позволяет нам использовать более сжатый код для вашей конкретной задачи, я бы все равно выбрал grep … | tail …, особенно если код будет сохраняться и сохраняться.


Улучшение

Я могу немного упростить вашу исходную команду, но ее форма grep … | tail … стенды:

# original, for comparison, commented out
# grep -v '^$' | tail -n 1

# simplified
grep . | tail -n 1
Другие вопросы по тегам