Если вам нужен точный эквивалент chomp, первым методом, который мне приходит в голову, является решение awk, которое уже выложил LatinSuD . Я добавлю некоторые другие методы, которые не реализуют, chompно реализуют некоторые общие задачи, которые chompчасто используются для.
Когда вы помещаете некоторый текст в переменную, все символы новой строки в конце удаляются. Таким образом, все эти команды выдают одинаковый однострочный вывод:
Если вы хотите добавить какой-либо текст в последнюю строку файла или вывода команды, это sedможет быть удобно. С GNU sed и большинством других современных реализаций это работает, даже если ввод не заканчивается новой строкой¹; однако, это не добавит новую строку, если ее еще не было.
sed '$ s/$/ done/'
¹ Однако это не работает со всеми реализациями sed: sed - это инструмент обработки текста, а файл, который не пуст и не заканчивается символом перевода строки, не является текстовым файлом.
Это не совсем эквивалентно chomp, так как chompудаляет не более одного завершающего символа новой строки.
Flimm
@Flimm Да, наиболее очевидным точным эквивалентом chompбудет решение awk, которое уже выложил LatinSuD. Но во многих случаях chompэто всего лишь инструмент для выполнения работы, и я предоставляю способы выполнения некоторых общих задач. Позвольте мне обновить мой ответ, чтобы уточнить это.
Жиль "ТАК - перестань быть злым"
1
Другой perlподход. Он считывает весь ввод в память, поэтому он не может быть хорошей идеей для больших объемов данных (используйте cuonglm или awkподход для этого):
$ printf "one\ntwo\n"| perl -0777pe's/\n$//'; echo " done"
one
two done
Это быстрое решение, так как нужно прочитать только один символ из файла, а затем удалить его напрямую ( truncate), не читая весь файл.
Однако при работе с данными из stdin (потока) все данные должны быть прочитаны. И это "потребляется", как только это прочитано. Нет возврата (как с усечением). Чтобы найти конец потока, нам нужно прочитать его до конца. В этот момент нет возможности вернуться назад к входному потоку, данные уже «использованы». Это означает, что данные должны храниться в некотором виде буфера до тех пор, пока мы не совпадем с концом потока, а затем что-то сделаем с данными в буфере.
Наиболее очевидным из решений является преобразование потока в файл и обработка этого файла. Но вопрос требует какого-то фильтра потока. Не об использовании дополнительных файлов.
переменная
Наивным решением было бы захватить весь ввод в переменную:
FilterOne(){ filecontents=$(cat; echo "x");# capture the whole input
filecontents=${filecontents%x};# Remove the "x" added above.
nl=$'\n';# use a variable for newline.
printf '%s'"${filecontents%"$nl"}";# Remove newline (if it exists).}
printf 'one\ntwo'|FilterOne; echo 1done
printf 'one\ntwo\n'|FilterOne; echo 2done
printf 'one\ntwo\n\n'|FilterOne; echo 3done
Память
Можно загрузить весь файл в память с помощью sed. В sed невозможно избежать завершающего перевода строки на последней строке. GNU sed может не печатать завершающий символ новой строки, но только если в исходном файле его уже нет. Так что нет, простой sed не может помочь.
За исключением GNU awk с -zопцией:
sed -z 's/\(.*\)\n$/\1/'
С помощью awk (любой awk) хлебать весь поток, и printfэто без завершающего перевода строки.
Загрузка всего файла в память может быть не очень хорошей идеей, поскольку она может занимать много памяти.
Две строки в памяти
В awk мы можем обработать две строки в цикле, сохранив предыдущую строку в переменной и напечатав текущую:
awk 'NR>1{print previous} {previous=$0} END {printf("%s",$0)}'
Прямая обработка
Но мы могли бы сделать лучше.
Если мы печатаем текущую строку без новой строки и печатаем новую только тогда, когда существует следующая строка, мы обрабатываем по одной строке за раз, и последняя строка не будет иметь завершающий символ новой строки:
chomp
, так какchomp
удаляет не более одного завершающего символа новой строки.chomp
будет решение awk, которое уже выложил LatinSuD. Но во многих случаяхchomp
это всего лишь инструмент для выполнения работы, и я предоставляю способы выполнения некоторых общих задач. Позвольте мне обновить мой ответ, чтобы уточнить это.Другой
perl
подход. Он считывает весь ввод в память, поэтому он не может быть хорошей идеей для больших объемов данных (используйте cuonglm илиawk
подход для этого):источник
Я поймал это где-то в репозитории github, но не могу найти где
удаление замыкающего пустые линии-SED
источник
Аннотация
Печатайте строки без новой строки, добавляйте новую строку, только если есть еще одна строка для печати.
Другие решения
Если мы работали с файлом, мы можем просто обрезать один символ из него (если он заканчивается на новой строке):
removeTrailNewline () {[[$ (tail -c 1 "$ 1")]] || truncate -s-1 "$ 1"; }
Это быстрое решение, так как нужно прочитать только один символ из файла, а затем удалить его напрямую (
truncate
), не читая весь файл.Однако при работе с данными из stdin (потока) все данные должны быть прочитаны. И это "потребляется", как только это прочитано. Нет возврата (как с усечением). Чтобы найти конец потока, нам нужно прочитать его до конца. В этот момент нет возможности вернуться назад к входному потоку, данные уже «использованы». Это означает, что данные должны храниться в некотором виде буфера до тех пор, пока мы не совпадем с концом потока, а затем что-то сделаем с данными в буфере.
Наиболее очевидным из решений является преобразование потока в файл и обработка этого файла. Но вопрос требует какого-то фильтра потока. Не об использовании дополнительных файлов.
переменная
Наивным решением было бы захватить весь ввод в переменную:
Память
Можно загрузить весь файл в память с помощью sed. В sed невозможно избежать завершающего перевода строки на последней строке. GNU sed может не печатать завершающий символ новой строки, но только если в исходном файле его уже нет. Так что нет, простой sed не может помочь.
За исключением GNU awk с
-z
опцией:С помощью awk (любой awk) хлебать весь поток, и
printf
это без завершающего перевода строки.Загрузка всего файла в память может быть не очень хорошей идеей, поскольку она может занимать много памяти.
Две строки в памяти
В awk мы можем обработать две строки в цикле, сохранив предыдущую строку в переменной и напечатав текущую:
Прямая обработка
Но мы могли бы сделать лучше.
Если мы печатаем текущую строку без новой строки и печатаем новую только тогда, когда существует следующая строка, мы обрабатываем по одной строке за раз, и последняя строка не будет иметь завершающий символ новой строки:
awk 'NR == 1 {printf ("% s", $ 0); далее}; {printf ("\ n% s", $ 0)} '
Или написано другим способом:
Или:
Так:
источник