![O vi adiciona silenciosamente uma nova linha (LF) no final do arquivo?](https://rvso.com/image/83704/O%20vi%20adiciona%20silenciosamente%20uma%20nova%20linha%20(LF)%20no%20final%20do%20arquivo%3F.png)
Tenho dificuldade em entender um comportamento estranho: o vi parece adicionar uma nova linha (ASCII: LF, pois é um Unix (AIX) sistema) 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 de 1 a 9, seguidos por uma nova linha (LF no meu sistema)) e apenas 9 na última linha (caracteres 1 a 9, sem nova linha/LF de terminação).
Mas parece que quando eu salvo é40bytes (em vez de 39) eod 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 dentro do 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 reabri-los com vi...
E se eu abrir foo2 (39 caracteres, sem terminação de nova linha) no vi eapenas faça :wq
sem editá-lo de forma alguma, diz que escreve 40 caracteres e o avanço de linha aparece!
Não consigo ter acesso a um vi mais recente (faço isso no AIX, vi (nãoVim) versão 3.10, eu acho? (sem "-versão" ou outro meio de saber)).
# 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 ~ indicava que a linha anterior NÃO terminava com uma nova linha.)
--
Editar:algumas atualizações adicionais e um pequeno resumo, com um grande agradecimento às respostas abaixo:
vi silenciosamente adiciona uma nova linha final no momento em que grava um arquivo que não a possui (a menos que o arquivo esteja vazio).
isso só acontece na hora de escrever! (ou seja, até você :w, você pode usar :e para verificar se o arquivo ainda está como você o abriu... (ou seja: ainda mostra "nome do arquivo" [última linha não está completa] N linha, M caractere). Quando você salva, uma nova linha é adicionada silenciosamente, sem um aviso específico (diz quantos bytes 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, me ajudou a encontrar uma maneira de saber quando a mudança realmente ocorre)
Esta (correção silenciosa) éPOSIXcomportamento! (veja a resposta @barefoot-io para referências)
Responder1
O POSIX requer esse comportamento, portanto não é incomum.
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 até oPOSIX ex-manual:
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 de {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 uma <nova linha> no final. A edição de outras formas de arquivos pode ser opcionalmente permitida por implementações ex.
A seção OUTPUT FILES do manual do 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:
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 <nova linha>. Embora 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 quando operam em arquivos de texto. Os utilitários padrão que possuem tais restrições sempre especificam "arquivos de texto" em suas seções STDIN ou INPUT FILES.
Uma sequência de zero ou mais caracteres não <nova linha> mais um caractere <nova linha> de terminação.
Essas definições no contexto desses 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 um arquivo de texto válido.
Embora esta postagem tenha feito referência à edição de 2013 do padrão POSIX,as estipulações relevantes também aparecem na edição muito mais antiga de 1997.
Por último, se você achar que a nova linha do ex é indesejável, você se sentirá profundamente violado pela edição intolerante da Sétima Edição do 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 que contenham caracteres não-ASCII.
Responder2
Este é o vi
comportamento esperado.
Seu arquivo tem uma última linha incompleta, estritamente falando (ou seja, 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 wc
, como sed
e similares, forneçam a saída esperada. Observe que vi
não se cala 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 vi
versão você está executando, você pode usar o :ve
comando. Isso 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á afirmando:
:ve
Version 3.10
Isso provavelmente significa que o AIX vi
é baseado no código-fonte SVR3.
Em qualquer caso, esse comportamento e a [Incomplete last line]
mensagem de aviso estão no vi
código-fonte legado de Bill Joy desde pelo menos 1979 e no AFAIK, retidos em todas as ramificações criadas a partir de lançamentos de código-fonte do System V, a partir dos quais Unix proprietários como o AIX foram construídos.
Cronologicamente falando, esse comportamento não é uma consequência da conformidade com POSIX, mas mais uma consequência da decisão original de Bill Joy de ser útil para usuários que editam arquivos de texto falsos e, uma década depois, da 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 for 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á linha não terminada para corrigir, vi
não anexa uma nova linha ao salvar o arquivo.
Responder3
O texto que não possui a nova linha final executada em um while
loop de shell resulta no descarte silencioso da última linha.
$ (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, sensato e adequado. A outra opção envolve saber e ter tempo para auditar todo o código shell que toca em texto sem a nova linha final ou correr o risco de perder a última linha do texto.
Responder4
Não me lembro de nenhum outro comportamento em que uma nova linha seja adicionada no final de um arquivo (usado vi
desde meados dos anos 80).
Indica ~
que há uma linha na tela que não faz parte do texto, e não que o arquivo não termina em nova linha. (Você pode ficar difícil de rastrear erros se colocar um ~
na última linha dos scripts de shell). Se você carregar um arquivo curto com uma nova linha no final, você mesmo verá ~
e refutará que seu pensamento indica texto sem final de nova linha.