curl / wget добавляет дополнительный ^ M, когда я добавляю данные в файл

503
AK_

Что-то жужжит мне об этом. Я пытаюсь загрузить два разных файла хостов в один, если я делаю это серьезно, тогда все в порядке, но когда я добавляю первый ко второму, ^Mв каждой строке файла хоста появляется странный символ .

Чтобы привести реальный пример здесь, что я делаю

wget https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts -O /etc/hosts && curl -s "https://raw.githubusercontent.com/CHEF-KOCH/CKs-FilterList/master/HOSTS/CK's-Spotify-HOSTS-FilterList.txt" >> /etc/hosts 

теперь /etc/hostsесть такие: enter image description here

но когда я делаю это отдельно, так

curl -s "https://raw.githubusercontent.com/CHEF-KOCH/CKs-FilterList/master/HOSTS/CK's-Spotify-HOSTS-FilterList.txt" > /tmp/hosts 

сейчас /tmp/hostsсовершенно нормально

enter image description here

Почему это происходит? Почему, когда я загружаю файлы отдельно, я не получаю неправильный перевод строки, но когда я объединяю их, я получаю это. Это должно быть 0x0a, а не 0x0a0x0d, почему это происходит?

Если вам нужно взглянуть на загружаемые файлы, вы можете перейти по ссылкам в командах:

  1. https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts
  2. https://raw.githubusercontent.com/CHEF-KOCH/CKs-FilterList/master/HOSTS/CK%27s-Spotify-HOSTS-FilterList.txt

РЕДАКТИРОВАТЬ: я попытался добавить только второй файл хоста в файл немой хостов, и то же самое произошло, поэтому мы можем не указывать, что первый файл является причиной проблемы

1
«Это должно быть 0x0a, а не 0x0a0x0d» - я думаю, что вы получите «0x0d 0x0a», а не наоборот. Или это действительно `0x0a 0x0d`? Kamil Maciorowski 6 лет назад 0
@KamilMaciorowski в зависимости от того, что есть на «^ M» в VIM. AK_ 6 лет назад 0

1 ответ на вопрос

3
Kamil Maciorowski

Ни один инструмент не добавляет ничего. Это довольно путаница (но не твоя вина) по нескольким причинам.

Есть два общих окончания строки:

  • Unix-стиль, один символ обозначается LF(или \nили0x0a ),
  • Стиль Windows, два символа CRLF(или \r\nили 0x0d 0x0a).

Вы скачиваете с двух разных URL. Кажется, сервер утверждает, что каждый файл есть text/plain, поэтому они должны использоватьCRLF . Второй (тот, кого вы curl) действительно использует CRLF, но первый (тот, кого вы wget) незаконно используетLF вместо этого .

Если вы загрузите только с первого URL (независимо от того, используете ли вы wgetили curl) и сохраните результат в hosts1файле, вы file hosts1получите:

hosts1: UTF-8 Unicode text 

(Это означает, что окончания строк LF, в противном случае это будетUTF-8 Unicode text, with CRLF line terminators ).

Если вы загружаете только со второго URL и сохраняете результат в hosts2файле, вы file hosts2получите:

hosts2: ASCII text, with CRLF line terminators 

Если вы загружаете оба файла в один и тот же файл, скажем, hosts12так, как вы это делаете, вы получите LFокончание строк для строк, пришедших с первого URL, иCRLF как окончание строк для строк, пришедших со второго URL.

На практике любой инструмент, который пытается определить, использует ли файл LFили CRLFпроверяет не более нескольких начальных строк, не все из них. Попробуйте, file hosts12и вы получите:

hosts12: UTF-8 Unicode text 

именно так и было hosts1. То же самое происходит, когда вы vim hosts12: редактор определяет окончания строк как LFоснованные на начале файла. Затем вы переходите к концу и видите множество символов ^M-s, обозначающих CRсимволы. vimпечатает их, потому что CRв этом случае он не считается частью правильного окончания строки.

Однако, когда вы vim hosts2, редактор правильно определяет окончания строки как CRLF. Те же CRсимволы, которые были напечатаны, как и ^Mраньше, теперь скрыты от вас, поскольку vimсчитают, что они являются частью правильных окончаний строк. Если вы добавите новую строку вручную, vimбудет использоваться конец строки в стиле Windows, даже если вы работаете в Unix. Вы можете подумать, что файл «совершенно нормальный», но это не обычный текстовый файл Unix.

Путаница заключается в том, что два файла на сервере используют разные окончания строк; затем vimпытается быть умным.

В Linux (Unix в целом) вы хотите, чтобы вы /etc/hostsиспользовали в LFкачестве окончания строки. См. Определения строки и символа новой строки в POSIX . Явно указано, что персонаж \n:

3.243 Символ новой строки ( <newline>) Символ,
который в выходном потоке указывает, что печать должна начинаться в начале следующей строки. Это символ, обозначенный '\n'на языке Си.

Я не думаю, что инструменты обязаны поддерживать \r\nтогда. Простое решение - запустить wget … && curl … >> …точно так же, как вы, а затем вызвать dos2unix /etc/hosts.

На вашем месте я бы работал с другим файлом, скажем /etc/hosts.tmp. Я хотел бы использовать wget, curl, dos2unix, chmod --reference=/etc/hosts, chown --reference=/etc/hosts. Только когда файл будет готов, я бы mvего заменил /etc/hosts. Эта особенность rename(2)актуальна:

Если он newpathуже существует, он будет атомарно заменен, так что не будет точки, в которой другой процесс, пытающийся получить доступ newpath, найдет его отсутствующим.

Таким образом, любой процесс найдет либо старый /etc/hosts(до mv), либо новый (после mv). Ваш текущий подход, непосредственная работа с которым /etc/hostsпозволяет сценарии, когда другой процесс находит файл неполным или с неправильными окончаниями строки около его конца.

удивительно объяснил, спасибо! Будет ли больно, если я буду держать вещи такими, как они? (будет ли хост-файл распознавать `CRLF` как правильную строку, заканчивающуюся в любом случае? AK_ 6 лет назад 0
@AK_ Я думаю, что нет такого понятия, как «файл, который распознает« CRLF »…», а «инструменты, которые распознают« CRLF »…». В моем Kubuntu, по крайней мере, некоторые из них не против смешанных окончаний строк. Я бы вообще не рассчитывал на это. См. Определения POSIX [line] (http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_206) и [символ новой строки] (http://pubs.opengroup.org/onlinepubs/9699919799/ basedefs / V1_chap03.html # tag_03_243). Явно указано, что символ `\ n`. Я не думаю, что инструменты обязаны поддерживать `\ r \ n` тогда. Kamil Maciorowski 6 лет назад 0

Похожие вопросы