Vi тихо добавляет новую строку (LF) в конце файла?

36

У меня проблемы с пониманием странного поведения: vi, кажется, добавляет новую строку (ASCII: LF, так как это система Unix ( AIX )) в конце файла, когда я НЕ специально его печатал.

Я редактирую файл как таковой в vi (стараясь не вводить новую строку в конце):

# vi foo   ## Which I will finish on the char "9" and not input a last newline, then `:wq`
123456789
123456789
123456789
123456789
~
~
  ## When I save, the cursor is just above the last "9", and no newline was added.

Я ожидаю, что vi сохранит его «как есть», поэтому будет иметь 39 байтов: 10 символов ASCII в каждой из первых трех строк (цифры от 1 до 9, за которыми следует новая строка (LF в моей системе)) и только 9 в последней строка (символы от 1 до 9, без новой строки / LF).

Но появляется, когда я сохраняю его, это 40 байтов (вместо 39), и od показывает завершающий LF :

# wc foo
       4       4      40 foo  ## I expected 39 here! as I didn't add the last newline
# od -a toto
0000000    1   2   3   4   5   6   7   8   9  lf   1   2   3   4   5   6
0000020    7   8   9  lf   1   2   3   4   5   6   7   8   9  lf   1   2
0000040    3   4   5   6   7   8   9  lf
0000050
     ## An "lf" terminates the file?? Did vi add it silently?

Если я создаю файл с помощью printf, делая то же самое, что и в vi, он работает как положено:

# ## I create a file with NO newline at the end:
# printf "123456789\n123456789\n123456789\n123456789" > foo2
# wc foo2  ## This one is as expected: 39 bytes, exactly as I was trying to do above with vi.
       3       4      39 foo  ## As expected, as I didn't add the last newline

  ## Note that for wc, there are only three lines!
  ## (So wc -l doesn't count lines; it counts the [newline] chars... Which is rather odd.)

# root@SPU0WMY1:~  ## od -a foo2
0000000    1   2   3   4   5   6   7   8   9  lf   1   2   3   4   5   6
0000020    7   8   9  lf   1   2   3   4   5   6   7   8   9  lf   1   2
0000040    3   4   5   6   7   8   9
0000047                                ## As expected, no added LF.

Оба файла (foo (40 символов) и foo2 (39 символов) выглядят одинаково, если я снова открываю их с помощью ...

И если я открываю foo2 (39 символов, без завершающей строки) в vi и просто :wqне редактирую его , он говорит, что записывает 40 символов, и появляется перевод строки!

У меня не может быть доступа к более новой версии vi (я делаю это в AIX, vi (не Vim ) версии 3.10, я думаю? (Без «-version» или других способов узнать это)).

# strings /usr/bin/vi | grep -i 'version.*[0-9]'
@(#) Version 3.10

Это нормально для vi (и, возможно, не в более поздней версии? Или Vim?), Чтобы тихо добавить новую строку в конце файла? (Я думал, что ~ указывает на то, что предыдущая строка НЕ ​​заканчивалась символом новой строки.)

-

Изменить: некоторые дополнительные обновления и немного резюме, с большой благодарностью за ответы ниже:

  • vi тихо добавляет завершающий символ новой строки в тот момент, когда пишет файл, в котором его не было (если файл не пуст).

  • это происходит только во время написания! (т. е. до тех пор, пока вы: w, вы можете использовать: e, чтобы убедиться, что файл все еще находится в том же состоянии, в котором вы его открывали ... (т.е. он по-прежнему показывает "имя файла" [Последняя строка не завершена] N строка, символ M). Когда вы сохраняете, новая строка добавляется без предупреждения, без особого предупреждения (она говорит, сколько байтов она сохраняет, но в большинстве случаев этого недостаточно, чтобы знать, что была добавлена ​​новая строка) (спасибо @jiliagre за то, что поговорили со мной о открыв сообщение vi, оно помогло мне найти способ узнать, когда действительно произойдет изменение)

  • Это (тихая коррекция) - поведение POSIX ! (см. @ barefoot-io answer для справок)

Оливье Дюлак
источник
Просто для полноты, какая версия AIX (полная версия).
EightBitTony
2
Я не знаю, что в AIX vi есть эта опция - появляется только vim
Джефф Шаллер
1
@JeffSchaller: спасибо за ссылку. К сожалению, у нативного vi нет ": set noeol" и даже опции -b для открытия в двоичном режиме ...
Оливье Дюлак
1
Вы можете получить viверсию или хотя бы понять ее происхождение, запустив :veкоманду.
Jlliagre
1
@ThomasDickey Действительно. По какой-то причине IBM сократила exстраницу руководства, где :verобычно описывается команда.
Jlliagre

Ответы:

28

Это ожидаемое viповедение.

Ваш файл имеет неполную последнюю строку, так что строго говоря (т.е. в соответствии со стандартом POSIX), это не текстовый файл, а двоичный файл.

vi это редактор текстовых файлов, а не двоичный, изящно исправляет его при сохранении.

Это позволяет другим инструментам текстовых файлов wc, sedтаким как лайки, предоставлять ожидаемый результат. Обратите внимание, что viне стоит молчать об этой проблеме:


$ printf "one\ntwo" >file     # Create a unterminated file
$ cat file                    # Note the missing newline before the prompt
one
two$ wc -l file               # wc ignores the incomplete last line
       1 file
$ sed '' file > file1
$ cat file1                   # so does a legacy sed
one
$ PATH=$(getconf PATH) sed  '' file
one                           # while a POSIX conformant sed warns you:
sed: Missing newline at end of file file.
two
$ vi file
one
two
~
~
~                             # vi tells you too about the issue
"file" [Incomplete last line] 2 lines, 7 characters

:w

"file" 2 lines, 8 characters  # and tells it writes two lines
                              # You'll even notice it writes one more
                              # character if you are a very shrewd observer :-)
:q
$ cat file                    # the file is now valid text
one
two
$ wc -l file                  # wc reports the expected number of lines
       2 file
$ sed '' file > file1         # sed works as expected
$ cat file1
one
two

Обратите внимание, чтобы получить некоторые подсказки о том, какую viверсию вы используете, вы можете использовать :veкоманду. Здесь видно, что я использую устаревший SVR4, определенно нет vim:

:ve
Version SVR4.0, Solaris 2.5.0

Судя по всему, ваш говорит:

:ve
Version 3.10

Это, вероятно, означает, что AIX viоснован на исходном коде SVR3.

В любом случае, это поведение и [Incomplete last line]предупреждающее сообщение были в унаследованном viисходном коде Билла Джоя с 1979 года и AFAIK, которые сохранялись во всех ветвях, созданных из выпусков исходного кода System V, из которых был создан проприетарный Unix, такой как AIX.

Хронологически говоря, это поведение не является следствием соответствия POSIX, а скорее следствием первоначального решения Билла Джоя быть полезным для пользователей, редактирующих фиктивные текстовые файлы, а затем, спустя десятилетие, решение комитета POSIX сохранить этот допуск.

Если вы используете edвместо vi, вы заметите, что первый более подробный о проблеме, по крайней мере, если вы edиз SVR3 или более новой ветви исходного кода:

$ ed file
'\n' appended
8
q

Также обратите внимание, что пустой файл является допустимым текстовым файлом, который содержит ноль строк. Поскольку в этом случае нет неопределенной строки для исправления, viпри сохранении файла не добавляется новая строка .

jlliagre
источник
1
Я полагаю, что вы ошиблись, принимая vim за vi;) Наследие vi гораздо менее многословно, чем это ...
Оливье Дюлак
@OlivierDulac Я не путаю их. Этот тест был выполнен с использованием устаревшего SVR4, viкак и OP, хотя на другом Unix. Это не тот vimили иной клон. Ответ обновлен, чтобы уточнить это.
Jlliagre
@OlivierDulac Хм, я только что заметил, что вы на самом деле ОП. Кажется, AIX использует более старую ветку System V для своей viреализации. Возможно SVR3. Вы уверены, что [Incomplete last line]при открытии файла сообщения нет?
Jlliagre
@OlivierDulac Эта ссылка, по-видимому, подразумевает, что это же сообщение может отображаться viреализацией AIX : www-01.ibm.com/support/docview.wss?uid=isg1IZ27694
jlliagre
Я постараюсь увидеть это завтра
Оливье Дюлак,
51

POSIX требует такого поведения, так что это не является чем-то необычным.

Из руководства по POSIX vi :

ВХОДНЫЕ ФАЙЛЫ

В разделе INPUT FILES команды ex приведено описание входных файлов, поддерживаемых командой vi.

Следуя указаниям руководства POSIX ex :

ВХОДНЫЕ ФАЙЛЫ

Входные файлы должны быть текстовыми файлами или файлами, которые будут текстовыми файлами, за исключением неполной последней строки, длина которой не превышает {LINE_MAX} -1 байт и не содержит символов NUL. По умолчанию любая незавершенная последняя строка должна обрабатываться так, как если бы она имела завершающий <newline>. Редактирование других форм файлов может быть необязательно разрешено ex-реализациями.

Раздел ВЫХОДНЫЕ ФАЙЛЫ руководства vi также перенаправляет на ex:

ВЫХОДНЫЕ ФАЙЛЫ

Выходные данные из ex должны быть текстовыми файлами.

Пара определений POSIX:

3.397 Текстовый файл

Файл, содержащий символы, организованные в ноль или более строк. Строки не содержат символов NUL, и ни одна из них не может превышать длину {LINE_MAX} байтов, включая символ <newline>. Хотя POSIX.1-2008 не делает различий между текстовыми файлами и двоичными файлами (см. Стандарт ISO C), многие утилиты производят только предсказуемый или значимый вывод при работе с текстовыми файлами. Стандартные утилиты, имеющие такие ограничения, всегда указывают «текстовые файлы» в своих разделах STDIN или INPUT FILES.

3.206 Линия

Последовательность из нуля или более не-символов <newline> плюс завершающий символ <newline>.

Эти определения в контексте этих выдержек из страницы руководства означают, что в то время как совместимая реализация ex / vi должна принимать искаженный текстовый файл, если единственной деформацией этого файла является отсутствующий заключительный символ новой строки, при записи буфера этого файла результатом должен быть действительный текстовый файл.

Хотя этот пост ссылается на выпуск стандарта POSIX 2013 года, соответствующие положения также появляются в гораздо более старой редакции 1997 года .

И наконец, если вы обнаружите, что бывшая новая версия приложения нежелательна, вы будете глубоко нарушены непереносимым изданием UNIX (1979) седьмого издания. Из руководства :

При чтении файла ed отбрасывает символы ASCII NUL и все символы после последней новой строки. Он отказывается читать файлы, содержащие не-ASCII символы.

Босиком И.О.
источник
спасибо, это ответ на мой вопрос. Я просто подожду еще несколько дней, если получится какой-то лучший ответ, но сейчас я чувствую, что вы можете быть принятым ответом.
Оливье Дюлак
Очень хорошо сделано на тщательно задокументированном ответе, прямо из спецификации! :)
Wildcard
1
@Wildcard, поведение предшествовало спецификации, хотя.
Jlliagre
@jlliagre, если у вас нет мемуаров от Билла Джоя или, возможно, создателя ex(не знаю его имени), я думаю, что спецификации POSIX настолько хороши, насколько можно ожидать. ;) Ближайший к «первоисточнику» к этому моменту, хотя это правда, они начинались как более или менее описания существующей функциональности.
Wildcard
3
@Wildcard exбыла написана в соавторстве с Биллом Джоем и Чаком Элли ( web.cecs.pdx.edu/~kirkenda/joy84.html .) Я не подвергаю сомнению спецификации POSIX и тот факт, что текущие viвыпуски следуют этому, я просто заявляю о поведении долго предшествует этому.
Jlliagre
1

Я не припоминаю никакого другого поведения, когда в конце файла добавляется символ новой строки (используется viс середины 80-х годов).

~Показывает , что линия на экране , который не является частью текста, а не о том , что файл не заканчивается символом новой строки. (Вам может быть трудно отследить ошибки, если вы поместите ~в последнюю строку сценариев оболочки). Если вы загрузите короткий файл с новой строкой в ​​конце, вы увидите ~себя и опровергнете, что ваша мысль указывает на текст, не заканчивающийся переводом новой строки.

Энтон
источник
Что меня удивляет, так это добавление новой строки ... Я ожидаю, что vi не добавит его молча, но, похоже, это так ... Я ищу объяснение этой позиции (тревожный факт: я открываю foo2 (без трейлинг LF) и просто: wq, он меняет свой контент ... так что он показывает мне что-то, но сохраняет другую вещь ... странно, если не сказать больше ^^
Оливье Дюлак
в его предшественнике ( ed) вы будете создавать строки и редактировать их, не добавляя символы. Я всегда думал о vi как о строковом редакторе. Но я понимаю ваше удивление.
Anthon
1

Текст, в котором неправильно отсутствует окончательный перевод строки через whileцикл оболочки, приводит к тому, что последняя строка молча отбрасывается.

$ (echo transaction 1; echo -n transaction 2) \
  | while read line; do echo $line; done
transaction 1
$ 

Обеспечение окончательного перехода на новую строку - это правильное, нормальное и правильное значение по умолчанию. Другой вариант заключается в знании и наличии времени на аудит всего кода оболочки, который касается текста, в котором отсутствует окончательный перевод строки, или на риск потери последней строки текста.

thrig
источник