O vi silenciosamente adiciona uma nova linha (LF) no final do arquivo?

34

Eu tenho dificuldade em entender um comportamento estranho: vi parece adicionar uma nova linha (ASCII: LF, como é um Unix ( AIX ) no final do arquivo, quando eu NÃO o digitei especificamente.

Eu edito o arquivo como tal no vi (tomando cuidado para não inserir uma nova linha no final):

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

Espero que o vi o salve "como está", para ter 39 bytes: 10 caracteres ASCII em cada uma das três primeiras linhas (números 1 a 9, seguidos por uma nova linha (LF no meu sistema)) e apenas 9 na última linha (caracteres de 1 a 9, sem nova linha de terminação / LF).

Mas, quando eu salvo, são 40 bytes (em vez de 39) e od mostra um LF de terminação :

# 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 eu criar o arquivo com um printf fazendo exatamente o que fiz dentro do vi, ele funcionará como esperado:

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

Ambos os arquivos (foo (40 caracteres) e foo2 (39 caracteres) aparecem exatamente da mesma forma se eu reabrir com o vi ...

E se eu abrir o foo2 (39 caracteres, sem nova linha de terminação) no vi e faça :wq sem editá-lo , ele diz que escreve 40 caracteres e o avanço de linha aparece!

Não consigo acessar um vi mais recente (faço isso no AIX, vi (não Vim ) versão 3.10 eu acho? (sem "-versão" ou outros meios de saber isso).

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

É normal que o vi (e talvez não na versão mais recente? Ou o Vim?) adicione silenciosamente uma nova linha no final de um arquivo? (Achei que o ~ indicava que a linha anterior NÃO terminara com uma nova linha.)

-

Edite: algumas atualizações adicionais e um resumo, com um grande agradecimento às respostas abaixo:

  • Adicione silenciosamente uma nova linha no final no momento em que ela escreve um arquivo que não a tenha (a menos que o arquivo esteja vazio).

  • só acontece na hora da escrita! (ou seja, até você: w, você pode usar: e para verificar se o arquivo ainda está como você o abriu ... (ex: ele ainda mostra "nome do arquivo" [Última linha não está completa] N linha, caractere M). Quando você salva, uma nova linha é silenciosamente adicionada, sem um aviso específico (ela diz quantos bytes ela salva, mas na maioria dos casos isso não é suficiente para saber que uma nova linha foi adicionada) (obrigado a @jiliagre por falar comigo sobre o abrindo a mensagem vi, isso me ajudou a encontrar uma maneira de saber quando a mudança realmente ocorre)

  • Esta (correção silenciosa) é o comportamento POSIX ! (veja @ barefoot-io answer para referências)

por Olivier Dulac 17.02.2016 / 16:35

4 respostas

29

Este é o comportamento vi esperado.

Seu arquivo tem uma última linha incompleta de maneira tão estrita (isto é, de acordo com o padrão POSIX), não é um arquivo de texto, mas um arquivo binário.

vi , que é um editor de arquivos de texto e não um binário, corrige quando você os salva.

Isso permite que outras ferramentas de arquivo de texto, como wc , sed e os gostos, forneçam a saída esperada. Observe que vi não está em silêncio sobre o 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

Note que, para obter algumas pistas sobre qual vi version você está executando, você pode usar o comando :ve . Ele mostra aqui que estou usando um SVR4 legado aqui, definitivamente não é vim :

:ve
Version SVR4.0, Solaris 2.5.0

Aparentemente, o seu está dizendo:

:ve
Version 3.10

Isso provavelmente significa que AIX vi é baseado no código-fonte SVR3.

Em qualquer caso, esse comportamento e a mensagem de aviso [Incomplete last line] estão no código-fonte vi do Bill Joy desde, pelo menos, 1979 e AFAIK, retidos em todas as ramificações criadas a partir das versões de código-fonte do System V. Unix proprietário como AIX foram construídos.

Cronologicamente falando, esse comportamento não é uma conseqüência da conformidade POSIX, mas mais uma consequência da decisão original de Bill Joy de ajudar os usuários a editar arquivos de texto falsos e, uma década depois, a decisão do comitê POSIX de manter essa tolerância .

Se você usar ed em vez de vi , notará que o primeiro é mais detalhado sobre o problema, pelo menos se o ed for do SVR3 ou o mais novo ramo de origem:

$ ed file
'\n' appended
8
q

Note também que um arquivo vazio é um arquivo de texto válido que contém zero linhas. Como não há nenhuma linha não terminada para corrigir, vi não acrescenta uma nova linha ao salvar o arquivo.

    
por 17.02.2016 / 20:37
52

O POSIX requer esse comportamento, portanto não é incomum.

Do manual do POSIX vi :

INPUT FILES

See the INPUT FILES section of the ex command for a description of the input files supported by the vi command.

Seguindo a trilha para o manual do POSIX ex :

INPUT FILES

Input files shall be text files or files that would be text files except for an incomplete last line that is not longer than {LINE_MAX}-1 bytes in length and contains no NUL characters. By default, any incomplete last line shall be treated as if it had a trailing <newline>. The editing of other forms of files may optionally be allowed by ex implementations.

A seção OUTPUT FILES do manual vi também redireciona para ex:

OUTPUT FILES

The output from ex shall be text files.

Um par de definições POSIX:

3.397 Text File

A file that contains characters organized into zero or more lines. The lines do not contain NUL characters and none can exceed {LINE_MAX} bytes in length, including the <newline> character. Although POSIX.1-2008 does not distinguish between text files and binary files (see the ISO C standard), many utilities only produce predictable or meaningful output when operating on text files. The standard utilities that have such restrictions always specify "text files" in their STDIN or INPUT FILES sections.

3.206 Line

A sequence of zero or more non- <newline> characters plus a terminating <newline> character.

Estas definições no contexto destes trechos de páginas de manual significam que, embora uma implementação ex / vi compatível deva aceitar um arquivo de texto malformado se a única deformidade desse arquivo for uma nova linha final ausente, ao escrever o buffer desse arquivo o resultado deve ser válido arquivo de texto.

Embora este post tenha mencionado a edição de 2013 do padrão POSIX, as estipulações relevantes também aparecem em muito edição mais antiga de 1997 .

Por último, se você achar que a nova linha de anúncios não é bem-vinda, você se sentirá profundamente violado pela edição intolerante da Seventh Edition UNIX (1979). Do manual :

When reading a file, ed discards ASCII NUL characters and all characters after the last newline. It refuses to read files containing non-ASCII characters.

    
por 17.02.2016 / 19:23
1

Não me lembro de nenhum outro comportamento que uma nova linha seja adicionada ao final de um arquivo (usando vi desde meados dos anos 80).

O ~ indica que uma linha na tela não faz parte do texto, não que o arquivo não termine em uma nova linha. (Você pode ter dificuldade em rastrear erros se colocar um ~ na última linha de scripts de shell). Se você carregar um arquivo curto com uma nova linha no final, verá o ~ e negará que o seu pensamento indica um texto que não termina na nova linha.

    
por 17.02.2016 / 16:42
1

O texto que não possui a última execução de linha através de um loop while do shell resulta na última linha sendo descartada silenciosamente.

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

Garantir que haja uma nova linha final é o padrão certo e correto. A outra opção envolve conhecer e ter tempo para auditar todo o código de shell que toca o texto sem a nova linha final ou arriscar perder a última linha do texto.

    
por 18.02.2016 / 23:05

Tags