tail -n1 в последней непустой строке
Я хотел добиться чего-то подобного
grep -v '^$' <myfile-with-blank-lines-at-the-end> | tail -n 1
По сути, у меня есть файл с уникальным номером, за которым следует запятая, и я хочу получить последний идентификатор, даже если под ним могут быть пустые строки. Цель состоит в том, чтобы использовать только хвост
1 ответ
Цель состоит в том, чтобы использовать только
tail
.
Выхода нет.
Анализ
Два используемых вами инструмента были созданы с учетом философии Unix:
- Пишите программы, которые делают одно и делают это хорошо.
- Напишите программы для совместной работы.
- Напишите программы для обработки текстовых потоков, потому что это универсальный интерфейс.
В вашем примере они работают вместе, передавая текстовый поток друг другу. Каждый делает свое "одно дело":
grep
вещь "S является „поиск файла по шаблону“,tail
Дело в том, что "копируем последнюю часть файла".
Эти вещи ортогональны; нет общего компонента.
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