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

36

Tenho problemas para entender um comportamento estranho: o vi parece adicionar uma nova linha (ASCII: LF, pois é um sistema Unix ( AIX )) no final do arquivo, quando 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 salve-o "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 nos últimos linha (caracteres de 1 a 9, sem nova linha / LF).

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

# 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 no vi, ele funcionará conforme o 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 iguais se eu os reabrir com vi ...

E se eu abrir foo2 (39 caracteres, sem nova linha final) no vi e simplesmente não :wqprecisar editá-lo , ele diz que escreve 40 caracteres e o feed de linha aparece!

Não posso ter acesso a um vi mais recente (eu faço isso no AIX, vi (não no Vim ) versão 3.10, acho? (Sem "versão" ou outro meio de conhecê-lo)).

# 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? (Eu pensei que o ~ indicava que a linha anterior NÃO terminava com uma nova linha.)

-

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

  • O vi adiciona silenciosamente uma nova linha à direita no momento em que grava um arquivo que não possui (a menos que o arquivo esteja vazio).

  • só o faz no momento da escrita! (ou seja, até você: w, você pode usar: e para verificar se o arquivo ainda está como você o abriu ... (ou seja: ele ainda mostra "filename" [A última linha não está completa] N line, caractere M). Quando você salva, uma nova linha é adicionada silenciosamente, sem um aviso específico (indica quantos bytes ela salva, mas na maioria dos casos 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)

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

Olivier Dulac
fonte
Apenas para completar, qual versão do AIX (versão completa).
EightBitTony 17/02
2
Eu não estou ciente do vi do AIX ter essa opção - aparece apenas para vim
Jeff Schaller
1
@ JeffSchaller: thx para o link. Infelizmente vi nativa não tem ": noeol set" nem mesmo a opção -b para abrir em modo binário ...
Olivier Dulac
1
Você pode obter a viversão ou pelo menos uma pista sobre sua origem executando o :vecomando
Jlliagre
1
@ThomasDickey Indeed. Por alguma razão, a IBM retirou a expágina do manual em que o :vercomando normalmente está documentado.
Jlliagre

Respostas:

28

Esse é o vicomportamento esperado .

Seu arquivo possui uma última linha incompleta, de maneira 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 arquivo de texto, não binário, corrige-o normalmente quando você o salva.

Isso permite que outras ferramentas de arquivo de texto, como wc, sedeo gosta de fornecer a saída esperada. Observe que vinão há 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

Observe que, para obter algumas dicas sobre qual viversão você está executando, você pode usar o :vecomando Mostra aqui que estou usando um SVR4 herdado aqui, definitivamente não vim:

:ve
Version SVR4.0, Solaris 2.5.0

Aparentemente, o seu está declarando:

:ve
Version 3.10

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

De qualquer forma, esse comportamento e a [Incomplete last line]mensagem de aviso estão no vicódigo-fonte herdado de Bill Joy desde pelo menos 1979 e no AFAIK, retidos em todas as ramificações criadas a partir dos lançamentos de código-fonte do System V, a partir dos quais o Unix proprietário como o AIX foi construído.

Cronologicamente falando, esse comportamento não é uma conseqüência da conformidade com o 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, com a decisão do comitê do POSIX de manter essa tolerância.

Se você usar em edvez de vi, notará que o primeiro é mais detalhado sobre o problema, pelo menos se você edé do SVR3 ou de uma ramificação de origem mais recente:

$ ed file
'\n' appended
8
q

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

jlliagre
fonte
1
Eu acredito que você confundir vim para vi;) legado vi é muito menos detalhada do que isso ...
Olivier Dulac
@OlivierDulac Não os estou confundindo. Este teste foi feito usando o legado do SVR4, viassim como o OP, embora em um Unix diferente. Este não é vimou outro clone. Resposta atualizada para esclarecer isso.
Jlliagre
@OlivierDulac Hmm, acabei de perceber que você é realmente o OP. Parece que o AIX está usando uma ramificação mais antiga do System V para sua viimplementação. Possivelmente SVR3. Tem certeza de que não há [Incomplete last line]mensagem quando você abre o arquivo?
Jlliagre
@OlivierDulac Este link parece implicar esta mesma mensagem pode ser exibida pelo AIX viimplementação: www-01.ibm.com/support/docview.wss?uid=isg1IZ27694
jlliagre
Eu vou tentar ver isso amanhã #
1144 Olivier Dulac
51

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

No manual do POSIX vi :

ARQUIVOS DE ENTRADA

Consulte a seção INPUT FILES do comando ex para obter uma descrição dos arquivos de entrada suportados pelo comando vi.

Seguindo a trilha para o manual POSIX ex :

ARQUIVOS DE ENTRADA

Os arquivos de entrada devem ser arquivos de texto ou arquivos que seriam arquivos de texto, exceto por uma última linha incompleta que não tenha mais que {LINE_MAX} -1 bytes de comprimento e não contenha caracteres NUL. Por padrão, qualquer última linha incompleta deve ser tratada como se tivesse um <newline> à direita. A edição de outras formas de arquivos pode opcionalmente ser permitida por implementações ex.

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

ARQUIVOS DE SAÍDA

A saída de ex deve ser arquivos de texto.

Um par de definições POSIX:

3.397 Arquivo de texto

Um arquivo que contém caracteres organizados em zero ou mais linhas. As linhas não contêm caracteres NUL e nenhuma pode exceder {LINE_MAX} bytes de comprimento, incluindo o caractere <newline>. Embora o POSIX.1-2008 não faça distinção entre arquivos de texto e arquivos binários (consulte o padrão ISO C), muitos utilitários produzem apenas resultados previsíveis ou significativos ao operar em arquivos de texto. Os utilitários padrão que possuem essas restrições sempre especificam "arquivos de texto" nas seções STDIN ou INPUT FILES.

3.206 Linha

Uma sequência de zero ou mais caracteres não <newline> mais um caractere <newline> final.

Essas definições no contexto desses trechos da página de manual significam que, enquanto uma implementação ex / vi em conformidade deve aceitar um arquivo de texto malformado, se a única deformidade desse arquivo for uma nova linha final ausente, ao gravar o buffer desse arquivo, o resultado deve ser um arquivo de texto válido.

Embora este post tenha feito referência à edição de 2013 do padrão POSIX, as estipulações relevantes também aparecem na edição de 1997 muito mais antiga .

Por fim, se você achar indesejável a nova linha de ex, sentir-se-á profundamente violado pelo intolerante ed da Sétima Edição UNIX (1979). Do manual :

Ao ler um arquivo, ed descarta caracteres ASCII NUL e todos os caracteres após a última nova linha. Ele se recusa a ler arquivos contendo caracteres não ASCII.

IO descalço
fonte
obrigado, isso responde a minha pergunta. vou esperar mais alguns dias para encontrar alguma resposta melhor, mas agora sinto que você pode ser a resposta aceita.
Olivier Dulac 17/02
Muito bem feito na resposta completamente documentada, diretamente das especificações! :)
Curinga
1
@ Wildcard, o comportamento precedeu as especificações.
Jlliagre
@ jlliagre, a menos que você tenha um livro de memórias de Bill Joy ou talvez o criador ex(não saiba o nome dele), acho que as especificações do POSIX são tão boas quanto se pode esperar. ;) Mais próximo da "fonte original" nesse ponto, mesmo que seja verdade que eles começaram como descrições mais ou menos da funcionalidade existente.
Curinga
3
@Wildcard exfoi co-escrito por Bill Joy e Chuck Alley ( web.cecs.pdx.edu/~kirkenda/joy84.html .) Eu não questiono o POSIX especificações eo fato atuais vilançamentos não segui-lo, eu só indicar o comportamento há muito tempo.
jlliagre
1

Não recordo nenhum outro comportamento que uma nova linha seja adicionada no final de um arquivo (usando videsde meados dos anos 80).

O ~indica que uma linha na tela que não é parte do texto, não que o arquivo não termina em uma nova linha. (Pode ser difícil rastrear erros se você colocar um ~na última linha de scripts de shell). Se você carregar um arquivo curto com uma nova linha no final, verá a ~si mesmo e negará que o seu pensamento indica um texto que não termina com a nova linha.

Anthon
fonte
o que me surpreende é a adição de uma nova linha ... Eu espero que o vi não a adicione silenciosamente, mas parece que sim ... Estou procurando uma explicação para essa atitude (o fato preocupante é: abro foo2 (sem arrastando LF) e apenas: wq, ele muda seu conteúdo ... então ele me mostra alguma coisa, mas poupa outra coisa ... estranho, para dizer o mínimo ^^
Olivier Dulac
em seu antecessor ( ed), você criaria linhas e as editaria, não acrescentando caracteres. Eu sempre pensei no vi como um editor orientado a linhas também. Mas eu entendo sua surpresa.
Anthon
1

O texto que carece indevidamente da execução de nova linha final em um whileloop de shell faz com que a última linha seja descartada silenciosamente.

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

Garantir que haja uma nova linha definitiva é o padrão correto, correto e adequado. A outra opção envolve conhecer e ter tempo para auditar todos os códigos de shell que tocam no texto sem a nova linha final ou correr o risco de perder a última linha do texto.

agitar
fonte