В вашем файле нет $
. $
- это символ, используемый для обозначения конца строки в регулярном выражении (точно так же, как ^
означает начало строки). В инструменте, который работает по одной строке за раз, конец строки, над которой он работает, также является концом строки, поэтому люди, использующие линейно-ориентированные инструменты, неправильно указывают $
как означающий конец строки, поскольку в контексте этого инструмента это то же самое. $
также используется в других инструментах (например, cat -E
) в качестве индикатора конца строки.
Некоторые термины / определения:
\r
- выход последовательность, используемая в сценариях для генерации или сопоставления символа CR
(возврат каретки) ^M
(control-M), ASCII 13 \n
- это escape-последовательность, используемая в сценариях для генерации или сопоставления LF
(перевод строки) ^J
(control-J), ASCII 10 $
- это метасимвол регулярного выражения, используемый в сценариях для обозначения end-of-string
(который часто также является концом ), а также используется инструментами для указания end-of-line
при отображении текста. \n
(т. е. только LF
) считается новой строкой в UNIX \r\n
(то есть CRLF
) считается новой строкой в DOS (см. Почему вывод моего инструмента перезаписывается и как я могу это исправить? )
Итак, когда вы сделайте:
$ printf 'foo\n' | cat -vE
foo$
, что не означает, что в конце foo
есть $
, просто cat
отображает $
, чтобы показать вам, где находится конец строки. Когда вы делаете:
$ printf 'foo\r\n' | cat -vE
foo^M$
^M
(control-M) явно показывает вам символ CR
(возврат каретки), сгенерированный \r
, но $
равен явно не показывает вам символ ^J
(control-J), который LF
(перевод строки) генерирует \n
, вместо этого он специально отображает другой символ $
, чтобы показать вам конец линия. Если бы он показывал вам ^J
s, то все было бы объединено в одну строку, которую было бы трудно прочитать. Рассмотрим простоту чтения:
$ printf 'the\nquick\nbrown\nfox\n' | cat -vE
the$
quick$
brown$
fox$
против, если результат был такой:
$ printf 'the\nquick\nbrown\nfox\n' | some_other_tool
the^Jquick^Jbrown^Jfox^J
Вы никогда не сможете выполнить одно из следующих действий:
$ printf 'foo\nbar\n' | sed 's/$//' | cat -vE
foo$
bar$
$ printf 'foo\nbar\n' | sed 's/\n//' | cat -vE
foo$
bar$
для удалите LF, так как sed уже использовал LF при чтении ввода, а $
сам по себе не является символом новой строки, это метасимвол, который позволяет вам сказать в вашем регулярном выражении «соответствует концу строки» (в данном случае, так как конец входной строки - это конец строки для sed по умолчанию).
Вы можете спросить - если sed потребляет LF при чтении ввода, то почему в конце каждой строки вывода есть LF? Ответ заключается в том, что sed добавляет LF к каждой выходной строке, так что он выводит действительный текстовый файл POSIX (без завершения LF у вас нет текстового файла POSIX и, следовательно, то, что любой последующий инструмент делает с ним, является неопределенным поведением).
Однако вы можете удалить LF, если используете инструмент, который не читает по одной строке за раз. GNU sed имеет опцию -z
для чтения текста, разделенного NUL, вместо текста, разделенного LF, и в этом режиме вы можете удалить LF
символов:
$ printf 'foo\nbar\n' | sed -z 's/\n//' | cat -vE
foobar$
, и теперь вы можете увидеть, как $
(метасимвол конца строки) отличается от \n
(escape-последовательность, соответствующая символу LF):
$ printf 'foo\nbar\n' | sed -z 's/$//' | cat -vE
foo$
bar$
$ printf 'foo\nbar\n' | sed -z 's/\n/<LF>/' | cat -vE
foo<LF>bar$
$ printf 'foo\nbar\n' | sed -z 's/$/<EOS>/' | cat -vE
foo$
bar$
<EOS>$
Итак, быстрый ответ на вопрос «как удалить LF с помощью sed?» это с GNU sed:
$ printf 'foo\nbar\n' | sed -z 's/\n//g'
foobar$
и если у вас нет GNU sed (или даже если вы его используете, так как вышеизложенное будет считывать весь ввод в память сразу, предполагая, что текстовый файл POSIX без NULs) в качестве входных данных), тогда вы должны просто использовать awk:
$ printf 'foo\nbar\n' | awk -v ORS= '1'
foobar$