Чи додає vi мовчки новий рядок (LF) в кінці файлу?


36

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

Я редагую файл як такий in 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, я думаю? (Немає "-вернення" чи іншого способу її знати)).

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

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

-

Редагувати: кілька додаткових оновлень і короткий підсумок, велике спасибі за відповіді нижче:

  • vi мовчки додайте зворотний новий рядок у момент, коли він записує файл, якого йому бракувало (якщо файл не порожній).

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

  • Це (тиха корекція) поведінка POSIX ! (див. відповідь @ босоніж-йо для довідок)


Тільки для повноти, яка версія AIX (повна версія).
EightBitTony

2
Мені невідомо, що у AIX ви є такий варіант - з'являється лише vim
Jeff Schaller

1
@JeffSchaller: thx для посилання. На жаль, у рідного vi немає ": встановити 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не додає новий рядок під час збереження файлу.


1
Я вважаю, що ви помиляєтеся vim для vi;) спадщина vi є набагато менш багатослівною, ніж ця ...
Олів'є Дулак

@OlivierDulac Я їх не плутаю. Цей тест був зроблений за допомогою спадщини SVR4 так viсамо, як це робить ОП, хоча і на іншому Unix. Це не той vimчи інший клон. Відповідь оновлена ​​для уточнення цього.
jlliagre

@OlivierDulac Хм, я щойно помітив, що ти насправді ОП. Здається, AIX використовує стару гілку системи 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 :

ВХОДНІ ФАЙЛИ

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

Розділ OUTPUT FILES у посібнику 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 року .

Нарешті, якщо ви побачите, що додаток до нового рядка було не бажаним, ви будете сильно порушені нетерпимим виданням (1979) UNIX сьомого видання. З посібника :

Під час читання файлу 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 як про редактор, орієнтований на рядки. Але я розумію ваше здивування.
Антон

1

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

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

Забезпечення кінцевої нової лінії - це правильний та здоровий і належний за замовчуванням. Інший варіант передбачає знати та мати час для аудиту всього коду оболонки, який торкається тексту, у якому відсутній остаточний новий рядок, або ризикувати втратою останнього рядка тексту.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.