Vi aggiunge silenziosamente una nuova riga (LF) alla fine del file?


36

Ho difficoltà a comprendere un comportamento strano: vi sembra aggiungere una nuova riga (ASCII: LF, in quanto si tratta di un sistema Unix ( AIX )) alla fine del file, quando NON l'ho digitato specificamente.

Modifico il file come tale in vi (facendo attenzione a non inserire una nuova riga alla fine):

# 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.

Mi aspetto che vi salvi "così com'è", in modo da avere 39 byte: 10 caratteri ASCII su ciascuna delle prime tre righe (numeri da 1 a 9, seguiti da una nuova riga (LF sul mio sistema)) e solo 9 sull'ultima linea (caratteri da 1 a 9, nessuna nuova riga / LF terminante).

Ma appare quando lo salvo è 40 byte (anziché 39) e od mostra un LF terminante :

# 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?

Se creo il file con un printf che fa esattamente quello che ho fatto all'interno di vi, funziona come previsto:

# ## 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.

Entrambi i file (foo (40 caratteri) e foo2 (39 caratteri) appaiono esattamente gli stessi se li riapro con vi ...

E se apro foo2 (39 caratteri, nessuna nuova riga che termina) in vi e faccio solo :wqsenza modificarlo , dice che scrive 40 caratteri e appare l'alimentatore!

Non riesco ad accedere a un vi più recente (lo faccio su AIX, vi (non Vim ) versione 3.10 penso? (Nessuna "versione" o altri mezzi per conoscerlo)).

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

È normale per vi (e forse non nella versione più recente? O Vim?) Aggiungere silenziosamente una nuova riga alla fine di un file? (Ho pensato che ~ indicava che la riga precedente NON si concludeva con una nuova riga.)

-

Modifica: alcuni aggiornamenti aggiuntivi e un po 'di riepilogo, con un grande ringraziamento alle risposte di seguito:

  • vi aggiunge silenziosamente una nuova riga finale nel momento in cui scrive un file a cui mancava (a meno che il file non sia vuoto).

  • lo fa solo al momento della scrittura! (vale a dire, fino a quando: w, è possibile utilizzare: e per verificare che il file sia ancora come lo si è aperto ... (ovvero: mostra ancora "nome file" [L'ultima riga non è completa] N linea, carattere M). Quando salvi, una nuova riga viene aggiunta silenziosamente, senza un avviso specifico (indica quanti byte salva, ma nella maggior parte dei casi non è sufficiente sapere che è stata aggiunta una nuova riga) (grazie a @jiliagre per avermi parlato del aprendo il messaggio vi, mi ha aiutato a trovare un modo per sapere quando si verifica davvero il cambiamento)

  • Questa (correzione silenziosa) è un comportamento POSIX ! (vedi la risposta @ barefoot-io per i riferimenti)


Solo per completezza, quale versione di AIX (versione completa).
EightBitTony,

2
Non sono a conoscenza del fatto che AIX abbia questa opzione - sembra solo vim
Jeff Schaller

1
@JeffSchaller: grazie per il link. Sfortunatamente il nativo vi non ha ": set noeol" e nemmeno l'opzione -b per aprire in modalità binaria ...
Olivier Dulac,

1
Potresti essere in grado di ottenere la viversione o almeno un indizio sulla sua origine eseguendo il :vecomando.
jlliagre,

1
@ThomasDickey Effettivamente. Per qualche motivo, IBM ha rimosso la expagina di manuale in cui il :vercomando è normalmente documentato.
jlliagre,

Risposte:


28

Questo è il vicomportamento previsto .

Il tuo file ha un'ultima riga incompleta, quindi in senso stretto (cioè secondo lo standard POSIX), non è un file di testo ma un file binario.

vi che è un editor di file di testo, non binario, lo corregge con grazia quando lo salvi.

Questo permette ad altri strumenti di file di testo come wc, sede simili per fornire i risultati attesi. Nota che vinon tace sul problema:


$ 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

Nota, per ottenere alcuni indizi su quale viversione stai utilizzando, puoi usare il :vecomando. Mostra qui che sto usando un SVR4 legacy qui, sicuramente no vim:

:ve
Version SVR4.0, Solaris 2.5.0

Apparentemente, il tuo sta affermando:

:ve
Version 3.10

Ciò significa probabilmente che AIX viè basato sul codice sorgente SVR3.

In ogni caso, questo comportamento e il [Incomplete last line]messaggio di avvertimento sono stati nel vicodice sorgente legacy di Bill Joy almeno dal 1979 e AFAIK, conservati in tutti i rami creati dalle versioni del codice sorgente di System V, da cui sono stati costruiti Unix proprietario come AIX.

Cronologicamente parlando, questo comportamento non è quindi una conseguenza della conformità POSIX ma piuttosto una conseguenza della decisione originale di Bill Joy di essere utile con gli utenti che modificano file di testo fasulli, e poi, un decennio dopo, la decisione del comitato POSIX di mantenere questa tolleranza.

Se usi edinvece di vi, noterai che il primo è più dettagliato sul problema, almeno se edprovieni da SVR3 o dal ramo di fonte più recente:

$ ed file
'\n' appended
8
q

Si noti inoltre che un file vuoto è un file di testo valido che sembra contenere zero righe. Poiché non esiste alcuna riga non terminata da correggere, vinon viene aggiunta una nuova riga durante il salvataggio del file.


1
Credo che sbagli vim per vi;) l'eredità vi è molto meno dettagliata di questa ...
Olivier Dulac,

@OlivierDulac Non li sto confondendo. Questo test è stato fatto usando l'eredità SVR4 viproprio come fa l'OP, anche se su un Unix diverso. Questo non è vimo un altro clone. Risposta aggiornata per chiarire questo.
jlliagre,

@OlivierDulac Hmm, ho appena notato che in realtà sei l'OP. Sembra che AIX stia usando un vecchio ramo System V per la sua viimplementazione. Forse SVR3. Sei sicuro che non ci siano [Incomplete last line]messaggi quando apri il file?
jlliagre,

@OlivierDulac Questo collegamento sembra implicare che questo stesso messaggio possa essere visualizzato dall'implementazione AIX vi: www-01.ibm.com/support/docview.wss?uid=isg1IZ27694
jlliagre,

Proverò a vederlo domani
Olivier Dulac,

51

POSIX richiede questo comportamento, quindi non è in alcun modo insolito.

Dal manuale POSIX vi :

FILE DI INGRESSO

Vedere la sezione INPUT FILES del comando ex per una descrizione dei file di input supportati dal comando vi.

Seguendo la traccia del manuale POSIX ex :

FILE DI INGRESSO

I file di input devono essere file di testo o file che sarebbero file di testo ad eccezione di un'ultima riga incompleta che non è più lunga di {LINE_MAX} -1 byte e non contiene caratteri NUL. Per impostazione predefinita, qualsiasi ultima riga incompleta deve essere trattata come se avesse una <newline> finale. La modifica di altre forme di file può essere facoltativamente consentita da ex implementazioni.

La sezione OUTPUT FILES del manuale vi reindirizza anche a ex:

FILE DI USCITA

L'output di ex deve essere un file di testo.

Una coppia di definizioni POSIX:

3.397 File di testo

Un file che contiene caratteri organizzati in zero o più righe. Le righe non contengono caratteri NUL e nessuna può superare i {LINE_MAX} byte di lunghezza, incluso il carattere <newline>. Sebbene POSIX.1-2008 non distingua tra file di testo e file binari (vedere lo standard ISO C), molte utility producono output prevedibili o significativi solo quando si opera su file di testo. Le utility standard che hanno tali restrizioni specificano sempre "file di testo" nelle loro sezioni STDIN o INPUT FILES.

Linea 3.206

Una sequenza di zero o più caratteri non <newline> più un carattere <newline> che termina.

Queste definizioni nel contesto di questi estratti di pagine di manuale significano che mentre un'implementazione conforme ex / vi deve accettare un file di testo non valido se l'unica deformità di quel file è una nuova riga finale assente, quando si scrive il buffer di quel file il risultato deve essere un file di testo valido.

Mentre questo post fa riferimento all'edizione 2013 dello standard POSIX, le disposizioni pertinenti compaiono anche nella versione molto più antica del 1997 .

Infine, se ritieni che l'appensione newline dell'ex sia sgradita, ti sentirai profondamente violato dall'intollerante edizione della Seventh Edition UNIX (1979). Dal manuale :

Durante la lettura di un file, ed elimina i caratteri ASCII NUL e tutti i caratteri dopo l'ultima riga. Rifiuta di leggere file contenenti caratteri non ASCII.


grazie, questo risponde alla mia domanda. aspetterò ancora qualche giorno nel caso in cui qualche risposta migliore cali, ma in questo momento sento che puoi essere la risposta accettata.
Olivier Dulac,

Molto ben fatto sulla risposta accuratamente documentata, direttamente dalle specifiche! :)
Wildcard il

1
@Wildcard, il comportamento ha comunque preceduto le specifiche.
jlliagre,

@jlliagre, a meno che tu non abbia un libro di memorie di Bill Joy o forse il creatore di ex(non conosco il suo nome), penso che le specifiche POSIX siano le migliori che ci si possa aspettare. ;) Più vicino alla "fonte originale" a questo punto, anche se è vero, hanno iniziato come più o meno descrizioni delle funzionalità esistenti.
Wildcard il

3
@Wildcard è exstata co-scritta da Bill Joy e Chuck Alley ( web.cecs.pdx.edu/~kirkenda/joy84.html .) Non metto in dubbio le specifiche POSIX e il fatto che le viversioni attuali lo seguano, dico semplicemente il comportamento lo precede da tempo.
jlliagre,

1

Non ricordo nessun altro comportamento che una nuova riga viene aggiunta alla fine di un file (utilizzando vidalla metà degli anni '80).

La ~indica che una linea sullo schermo che non fa parte del testo, non che il file non termina con un ritorno a capo. (Può essere difficile rintracciare gli errori se si inserisce un ~nell'ultima riga di script di shell). Se carichi un breve file con una nuova riga alla fine, vedrai ~te stesso e confuterai che il tuo pensiero indica un testo senza fine di riga.


ciò che mi sorprende è l'aggiunta di una nuova riga ... Mi aspetto che non lo aggiunga in silenzio, ma sembra che lo faccia ... Sto cercando una spiegazione di questo atteggiamento (il fatto preoccupante è: apro foo2 (senza trailing LF) e solo: wq, CAMBIA il suo contenuto ... quindi mi mostra qualcosa ma salva un'altra cosa ... strano, per non dire altro ^^
Olivier Dulac,

nel suo predecessore ( ed) è necessario creare linee e modificarle, non aggiungendo caratteri. Ho sempre pensato a vi anche come editor orientato alla linea. Ma capisco la tua sorpresa.
Anthon,

1

Il testo a cui manca in modo improprio la nuova riga finale che passa attraverso un whileciclo di shell fa sì che l'ultima riga venga silenziosamente scartata.

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

Garantire che esista una newline definitiva è il default giusto, sano e corretto. L'altra opzione prevede la conoscenza e il tempo di controllare tutto il codice della shell che tocca il testo privo della nuova riga finale o di rischiare di perdere l'ultima riga del testo.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.