Как заменить символ новой строки (\n) с помощью sed?
Как я могу заменить новую строку (\n
) с помощью команды sed?
Я безуспешно пытался:
sed 's#\n# #g' file
sed 's#^$# #g' file
Как мне это исправить?
45 ответов
Используйте это решение с GNU sed
:
sed ':a;N;$!ba;s/\n/ /g' file
Это прочитает весь файл в цикле, а затем заменит символы новой строки пробелом.
Объяснение:
- Создать ярлык через
:a
, - Добавить текущую и следующую строку в пространство шаблона с помощью
N
, - Если мы до последней строки, переход к созданной метке
$!ba
($!
означает не делать это в последней строке, так как должен быть один последний перевод строки). - Наконец, подстановка заменяет каждую новую строку пробелом в пространстве образца (который является целым файлом).
Вот кроссплатформенный совместимый синтаксис, который работает с BSD и OS X sed
(согласно комментарию @Benjie):
sed -e ':a' -e 'N' -e '$!ba' -e 's/\n/ /g' file
Как видите, используя sed
для этого в противном случае простая проблема проблематична. Для более простого и адекватного решения см. Этот ответ.
Использование tr
вместо?
tr '\n' ' ' < input_filename
или полностью удалите символы новой строки:
tr -d '\n' < input.txt > output.txt
или если у вас есть версия GNU (с ее длинными опциями)
tr --delete '\n' < input.txt > output.txt
Быстрый ответ:
sed ':a;N;$!ba;s/\n/ /g' file
- : создать ярлык "а"
- N добавить следующую строку в пространство образца
- $! если не последняя строка, то ответвление (переход к метке "a")
- s заменить, / \n / regex для новой строки, / / через пробел, / g глобальное совпадение (столько раз, сколько это возможно)
sed будет перебирать шаги с 1 по 3, пока не достигнет последней строки, поместив все строки в шаблонное пространство, где sed заменит все символы \n
Альтернативы:
Все альтернативы, в отличие от sed, не должны достигать последней строки, чтобы начать процесс
с баш, медленно
while read line; do printf "%s" "$line "; done < file
с perl, sed- подобной скоростью
perl -p -e 's/\n/ /' file
с tr, быстрее чем sed, может заменить только один символ
tr '\n' ' ' < file
с пастой, tr- like speed, может заменить только один символ
paste -s -d ' ' file
с awk, tr- like скоростью
awk 1 ORS=' ' file
Другая альтернатива, такая как "echo $(
Длинный ответ от sed FAQ 5.10:
5.10. Почему я не могу сопоставить или удалить новую строку, используя escape \n
последовательность? Почему я не могу сопоставить 2 или более строк, используя \n?
\ N никогда не будет соответствовать символу новой строки в конце строки, потому что
символ новой строки всегда удаляется, прежде чем строка помещается в
образец пространства. Чтобы получить 2 или более строк в пространство шаблона, используйте
команда "N" или что-то подобное (например, "H;...;g;").
Sed работает так: sed читает по одной строке за раз, отрубает
завершающий перевод строки, помещает то, что осталось в пространство шаблона, где
Сценарий Sed может адресовать или изменять его, и когда образец пространства
печатается, добавляет новую строку в стандартный вывод (или в файл). Если
пространство образца полностью или частично удаляется с помощью "d" или "D",
Новая строка не добавляется в таких случаях. Таким образом, сценарии, такие как
sed 's/\n//' file # to delete newlines from each line
sed 's/\n/foo\n/' file # to add a word to the end of each line
НИКОГДА не будет работать, потому что завершающий символ новой строки удаляется раньше
линия помещается в пространство шаблона. Для выполнения вышеуказанных задач,
используйте вместо этого один из этих сценариев:
tr -d '\n' < file # use tr to delete newlines
sed ':a;N;$!ba;s/\n//g' file # GNU sed to delete newlines
sed 's/$/ foo/' file # add "foo" to end of each line
Поскольку версии sed, отличные от GNU sed, имеют ограничения на размер
буфер шаблонов, утилита Unix 'tr' должна быть здесь предпочтительнее.
Если последняя строка файла содержит символ новой строки, GNU sed добавит
этот перевод строки в вывод, но удалить все остальные, тогда как tr будет
удалить все новые строки.
Чтобы сопоставить блок из двух или более строк, есть 3 основных варианта:
(1) используйте команду 'N', чтобы добавить следующую строку в пространство шаблона;
(2) используйте команду 'H' как минимум дважды, чтобы добавить текущую строку
в пространство удержания, а затем получить линии из пространства удержания
с х, г или G; или (3) использовать диапазоны адресов (см. раздел 3.3 выше)
чтобы сопоставить строки между двумя указанными адресами.
Варианты (1) и (2) поместят \n в пространство шаблона, где оно
могут быть адресованы по желанию ('s/ABC\nXYZ/ алфавит /g'). Один пример
использование "N" для удаления блока строк приведено в разделе 4.13.
("Как удалить блок из определенных последовательных строк?"). это
пример можно изменить, изменив команду delete на что-то
иначе, например, "p" (печать), "i" (вставка), "c" (изменение), "a" (добавление),
или 's' (заменить).
Выбор (3) не помещает \n в пространство шаблона, но делает
сопоставить блок последовательных строк, поэтому может случиться так, что вы не
даже нужно \n, чтобы найти то, что вы ищете. С ГНУ СЭД
версия 3.02.80 теперь поддерживает этот синтаксис:
sed '/start/,+4d' # to delete "start" plus the next 4 lines,
в дополнение к традиционному диапазону '/from here/,/to there/{...}'
адреса, возможно, можно полностью избежать использования \n.
У GNU SED есть опция -z
для разделенных нулями записей (строк). Вы можете просто позвонить:
sed -z 's/\n/ /g'
Более короткая альтернатива awk:
awk 1 ORS=' '
объяснение
Программа awk состоит из правил, которые состоят из условных кодовых блоков, а именно:
condition { code-block }
Если кодовый блок опущен, используется значение по умолчанию: { print $0 }
, Таким образом 1
интерпретируется как истинное условие и print $0
выполняется для каждой строки.
когда awk
читает входные данные, которые разбивает его на записи на основе значения RS
(Record Separator), который по умолчанию является новой строкой, таким образом awk
будет по умолчанию анализировать ввод по строке. Разделение также включает в себя удаление RS
из входной записи.
Теперь, при печати записи, ORS
(Выходной разделитель записей) добавляется к нему, по умолчанию снова переводится строка. Так что, изменив ORS
в пробел все строки переводятся в пробелы.
Версия Perl работает так, как вы ожидали.
perl -i -p -e 's/\n//' file
Как указано в комментариях, стоит отметить, что это редактирует на месте. -i.bak
перед заменой даст вам резервную копию исходного файла на случай, если ваше регулярное выражение окажется не таким умным, как вы думали.
Кто нуждается sed
? Здесь bash
путь:
cat test.txt | while read line; do echo -n "$line "; done
Чтобы заменить все символы новой строки пробелами, используя awk, без чтения всего файла в память:
awk '{printf "%s ", $0}' inputfile
Если вы хотите последний перевод строки:
awk '{printf "%s ", $0} END {printf "\n"}' inputfile
Вы можете использовать символ, отличный от пробела:
awk '{printf "%s|", $0} END {printf "\n"}' inputfile
Три вещи.
tr
(или жеcat
и т. д.) абсолютно не нужно. (ГНУ)sed
и (GNU)awk
в сочетании может выполнять 99,9% любой обработки текста, которая вам нужна.поток!= строка на основе.
ed
это линейный редактор.sed
не является. Посмотрите лекцию sed для получения дополнительной информации о разнице. Большинство людей путаютsed
быть основанным на строках, потому что по умолчанию он не очень жадный в сопоставлении с образцом для совпадений SIMPLE - например, при поиске и замене образца по одному или двум символам он по умолчанию заменяет только первое найденное совпадение (если не указано иное глобальной командой). Не было бы даже глобальной команды, если бы она основывалась на строках, а не на STREAM, потому что она будет оценивать только строки одновременно. Попробуйте запуститьed
; Вы заметите разницу.ed
Это довольно полезно, если вы хотите перебирать определенные строки (например, в цикле for), но в большинстве случаев вам просто нужноsed
,Что, как говорится,
sed -e '{:q;N;s/\n/ /g;t q}' file
прекрасно работает в GNU
sed
версия 4.2.1. Приведенная выше команда заменит все новые строки пробелами. Это некрасиво и немного громоздко набирать, но работает просто отлично.{}
Это можно не учитывать, поскольку они включены только по соображениям здравомыслия.
Почему я не нашел простого решения с awk
?
awk '{printf $0}' file
printf
будет печатать каждую строку без новых строк, если вы хотите разделить исходные строки пробелом или другим:
awk '{printf $0 " "}' file
Простое для понимания решение
У меня была эта проблема. Главное, что мне нужно было решение для работы с BSD (Mac OS X) и GNU (Linux и Cygwin). sed
а также tr
:
$ echo 'foo
bar
baz
foo2
bar2
baz2' \
| tr '\n' '\000' \
| sed 's:\x00\x00.*:\n:g' \
| tr '\000' '\n'
Выход:
foo
bar
baz
(завершающий перевод строки)
Он работает на Linux, OS X и BSD - даже без поддержки UTF-8 или с дерьмовым терминалом.
использование
tr
поменять строку с другим персонажем.NULL
(\000
или же\x00
) это хорошо, потому что не нуждается в поддержке UTF-8 и вряд ли будет использоваться.использование
sed
чтобы соответствоватьNULL
использование
tr
поменять местами дополнительные символы новой строки, если они вам нужны
Ответ с меткой...
Как заменить символ новой строки (\n) с помощью sed?
... не работает в freebsd 7.2 в командной строке:
(эхо фу; эхо бар) | sed ':a;N;$!ba;s/\n/ /g' sed: 1: ":a;N;$!ba;s/\n/ /g": неиспользуемая метка 'a;N;$!ba;s/\n/ /g' Foo бар
Но если вы помещаете скрипт sed в файл или используете -e, чтобы "собрать" скрипт sed...
> (эхо фу; эхо бар) | sed -e: -e N -e '$!ba' -e 's/\n/ /g' фу бар
или же...
> cat > x.sed << eof
:a
N
$!ba
s/\n/ /g
eof
> (echo foo; echo bar) | sed -f x.sed
foo bar
Возможно sed в OS X похож.
Если вам не повезло иметь дело с окончаниями строк Windows, вам нужно удалить \r
и \n
tr '[\r\n]' ' ' < $input > $output
Я не эксперт, но я думаю, в sed
вам сначала нужно добавить следующую строку в пространство шаблонов, используя bij "N
". Из раздела" Пространство многострочных шаблонов "в" Расширенных командах sed "книги sed & awk (Дейл Догерти и Арнольд Роббинс; О'Рейли, 1997; стр. 107 в превью):
Многострочная команда Next (N) создает многострочное пространство шаблонов, читая новую строку ввода и добавляя ее к содержимому пространства шаблонов. Исходное содержимое пространства шаблона и новая строка ввода разделены новой строкой. Встроенный символ новой строки можно сопоставить в шаблонах с помощью escape-последовательности "\ n". В многострочном шаблонном пространстве метасимвол "^" соответствует самому первому символу пространства шаблонов, а не символу (символам) после любой встроенной новой строки (строк). Точно так же "$" соответствует только последней новой строке в пространстве шаблона, а не любой внедренной новой строке (строках). После выполнения команды Next управление передается последующим командам в сценарии.
От man sed
:
[2addr] Н
Добавьте следующую строку ввода в пространство шаблона, используя встроенный символ новой строки, чтобы отделить добавленный материал от исходного содержимого. Обратите внимание, что текущий номер строки изменяется.
Я использовал это для поиска (нескольких) плохо отформатированных файлов журналов, в которых строка поиска может быть найдена в "потерянной" следующей строке.
Вы также можете использовать этот метод
sed 'x;G;1!h;s/\n/ /g;$!d'
объяснение
x - which is used to exchange the data from both space (pattern and hold).
G - which is used to append the data from hold space to pattern space.
h - which is used to copy the pattern space to hold space.
1!h - During first line won't copy pattern space to hold space due to \n is
available in pattern space.
$!d - Clear the pattern space every time before getting next line until the
last line.
Поток:
Когда первая строка получает входные данные, выполняется обмен, поэтому 1 переходит в пространство пробелов и \n переходит в пространство шаблонов, затем добавляется пространство удержаний в пространство шаблонов, а затем выполняется подстановка и удаляется пространство шаблонов.
Во время второй линии происходит обмен, 2 идет на пробел и 1 приходит на пробел, затем G
добавить пространство удержания в пространство образца, затем h
скопируйте образец к нему, и замена сделана и удалена. Эта операция продолжается до тех пор, пока eof не будет достигнут, затем выведите точный результат.
В ответ на приведенное выше решение "tr" в Windows (возможно, с использованием версии tr для Gnuwin32) предлагаемое решение:
tr '\n' ' ' < input
не работал для меня, он либо по ошибке, либо фактически заменил \n w/ '' по какой-то причине.
Используя другую функцию tr, опция -d "delete" сработала:
tr -d '\n' < input
или '\ r \ n' вместо '\ n'
Я использовал гибридный подход для обхода новой строки, используя tr для замены новой строки на вкладки, а затем заменяя вкладки тем, что я хочу. В этом случае, "
"так как я пытаюсь генерировать разрывы HTML.
echo -e "a\nb\nc\n" |tr '\n' '\t' | sed 's/\t/ <br> /g'`
Пуленепробиваемое решение. Безопасность двоичных данных и POSIX-совместимость, но медленная.
POSIX sed требует ввода в соответствии с текстовым файлом POSIX и определениями строк POSIX, поэтому недопустимые байты и слишком длинные строки не допускаются, и каждая строка должна заканчиваться новой строкой (включая последнюю строку). Это затрудняет использование sed для обработки произвольных входных данных.
Следующее решение исключает sed и вместо этого преобразует входные байты в восьмеричные коды, а затем снова в байты, но перехватывает восьмеричный код 012 (новая строка) и выводит строку замены вместо нее. Насколько я могу судить, решение является POSIX-совместимым, поэтому оно должно работать на самых разных платформах.
od -A n -t o1 -v | tr ' \t' '\n\n' | grep . |
while read x; do [ "0$x" -eq 012 ] && printf '<br>\n' || printf "\\$x"; done
Справочная документация POSIX: sh, язык команд оболочки, od, tr, grep, read, [, printf.
И то и другое read
, [
, а также printf
встроены, по крайней мере, в bash, но это, вероятно, не гарантируется POSIX, поэтому на некоторых платформах может быть так, что каждый входной байт будет запускать один или несколько новых процессов, что замедляет работу. Даже в bash это решение достигает только 50 кБ / с, поэтому оно не подходит для больших файлов.
Протестировано на Ubuntu (bash, dash и busybox), FreeBSD и OpenBSD.
Находит и заменяет с помощью разрешения \n
sed -ie -z 's/Marker\n/# Marker Comment\nMarker\n/g' myfile.txt
маркер
становится
# Маркерный комментарий
маркер
В некоторых ситуациях вы можете изменить RS
на другую строку или символ. Таким образом, \n доступен для sub/gsub:
$ gawk 'BEGIN {RS="dn" } {gsub("\n"," ") ;print $0 }' file
Сила сценариев оболочки заключается в том, что если вы не знаете, как это сделать одним способом, вы можете сделать это другим способом. И во многих случаях вам нужно учитывать больше вещей, чем принимать комплексное решение простой проблемы.
Что касается того, что gawk работает медленно... и читает файл в память, я этого не знаю, но мне кажется, что gawk работает с одной строкой одновременно и работает очень-очень быстро (не так быстро, как некоторые другие, но время для написания и тестирования также имеет значение).
Я обрабатываю МБ и даже ГБ данных, и единственное ограничение, которое я нашел, - это размер строки.
Вы могли бы использовать xargs
- это заменит \n
с пробелом по умолчанию.
Тем не менее, это будет иметь проблемы, если ваш вклад имеет какой-либо случай unterminated quote
Например, если кавычки на данной строке не совпадают.
Именно sed вводит новые строки после "нормальной" замены. Сначала он обрезает символ новой строки, затем обрабатывает в соответствии с вашими инструкциями, затем вводит новую строку.
Используя sed, вы можете заменить "конец" строки (не символ новой строки) после обрезки выбранной строкой для каждой строки ввода; но sed выведет разные строки. Например, предположим, что вы хотите заменить "конец строки" на "===" (более общий, чем замена на один пробел):
PROMPT~$ cat <<EOF |sed 's/$/===/g'
first line
second line
3rd line
EOF
first line===
second line===
3rd line===
PROMPT~$
Чтобы заменить символ новой строки на строку, вы можете, хотя и неэффективно, использовать tr, как указано выше, чтобы заменить символы новой строки на "специальный символ", а затем использовать sed, чтобы заменить этот специальный символ строкой, которую вы хотите.,
Например:
PROMPT~$ cat <<EOF | tr '\n' $'\x01'|sed -e 's/\x01/===/g'
first line
second line
3rd line
EOF
first line===second line===3rd line===PROMPT~$
Решение, которое мне особенно нравится, состоит в том, чтобы добавить весь файл в область хранения и заменить все новые строки в конце файла:
$ (echo foo; echo bar) | sed -n 'H;${x;s/\n//g;p;}'
foobar
Тем не менее, кто-то сказал мне, что пространство удержания может быть конечным в некоторых реализациях sed.
Вы также можете использовать стандартный текстовый редактор:
printf '%s\n%s\n%s\n' '%s/$/ /' '%j' 'w' | ed -s file
Примечание: это сохраняет результат обратно в file
,
Как с sed
Это решение страдает от необходимости загружать весь файл в память в первую очередь.
В Mac OS X (с использованием FreeBSD sed):
# replace each newline with a space
printf "a\nb\nc\nd\ne\nf" | sed -E -e :a -e '$!N; s/\n/ /g; ta'
printf "a\nb\nc\nd\ne\nf" | sed -E -e :a -e '$!N; s/\n/ /g' -e ta
Замените символы новой строки любой строкой, а также замените последний символ новой строки.
Чистый tr
решения можно заменить только одним символом, а чистый sed
Решения не заменяют последнюю новую строку ввода. Следующее решение устраняет эти проблемы и, по-видимому, безопасно для двоичных данных (даже с языковым стандартом UTF-8):
printf '1\n2\n3\n' |
sed 's/%/%p/g;s/@/%a/g' | tr '\n' @ | sed 's/@/<br>/g;s/%a/@/g;s/%p/%/g'
Результат:
1<br>2<br>3<br>