Usando sistemas de controle de versão, fico aborrecido com o barulho quando o diff diz No newline at end of file
.
Então, eu estava pensando: como adicionar uma nova linha no final de um arquivo para se livrar dessas mensagens?
bash
shell
text-processing
newlines
k0pernikus
fonte
fonte
Respostas:
Para higienizar recursivamente um projeto, eu uso este oneliner:
Explicação:
git ls-files -z
lista arquivos no repositório. É necessário um padrão opcional como parâmetro adicional, que pode ser útil em alguns casos, se você quiser restringir a operação a determinados arquivos / diretórios. Como alternativa, você pode usarfind -print0 ...
ou programas semelhantes para listar os arquivos afetados - apenas certifique-se de que ele emiteNUL
entradas delimitadas.while IFS= read -rd '' f; do ... done
itera pelas entradas, manipulando com segurança os nomes de arquivos que incluem espaços em branco e / ou novas linhas.tail -c1 < "$f"
lê o último caractere de um arquivo.read -r _
sai com um status de saída diferente de zero se uma nova linha à direita estiver ausente.|| echo >> "$f"
anexa uma nova linha ao arquivo se o status de saída do comando anterior for diferente de zero.fonte
find -name \*.java | while read f; do tail -n1 $f | read -r _ || echo >> $f; done
git ls-files
qual ainda o salvará de editar arquivos que não são rastreados no controle de versão.IFS=
para desarmar o separador é bom para preservar o espaço em branco ao redor. As entradas terminadas nulas são relevantes apenas se você tiver arquivos ou diretórios com uma nova linha em seu nome, o que parece meio improvável, mas é a maneira mais correta de lidar com o caso genérico, eu concordo. Como uma pequena ressalva: a-d
opção pararead
não está disponível no POSIX sh.tail -n1 < "$f"
para evitar problemas com nomes de arquivos iniciados por-
(tail -n1 -- "$f"
não funciona para o arquivo chamado-
). Você pode esclarecer que a resposta agora é específica para zsh / bash.Aqui você vai :
E como alternativa para o OS X
sed
:Isso adiciona
\n
no final do arquivo apenas se ele ainda não terminar com uma nova linha. Portanto, se você executá-lo duas vezes, ele não adicionará outra nova linha:fonte
man sed
:$ Match the last line.
Mas talvez funcione apenas por acidente. Sua solução também funciona.$
corresponde à última linha, por que não adiciona outra nova linha a uma string que já contém uma nova linha?$
. Dentro de uma regex, como no formulário/<regex>/
, ela tem o significado usual de "correspondência de final de linha". Caso contrário, usado como um endereço, sed atribui o significado especial "última linha no arquivo". O código funciona porque sed, por padrão, acrescenta uma nova linha à saída, se ainda não estiver lá. O código "$ a \" diz apenas "corresponde à última linha do arquivo e não adiciona nada a ele". Mas implicitamente, o sed adiciona a nova linha a cada linha que processa (como esta$
linha), se ainda não estiver lá./regex/
dá um significado diferente. As páginas de manual do FreeBSD são um pouco mais informativo, eu penso: freebsd.org/cgi/man.cgi?query=sedDar uma olhada:
então
echo "" >> noeol-file
deve fazer o truque. (Ou você quis pedir para identificar esses arquivos e corrigi-los?)edit removeu o
""
deecho "" >> foo
(veja o comentário de @ yuyichao) edit2 adicionou""
novamente ( mas veja o comentário de @Keith Thompson)fonte
""
não é necessário (pelo menos para bash) etail -1 | wc -l
pode ser usado para descobrir o arquivo sem uma nova linha no final""
não é necessário para o bash, mas viecho
implementações que não imprimem nada quando invocadas sem argumentos (embora nenhuma das que eu possa encontrar agora faça isso).echo "" >> noeol-file
é provavelmente um pouco mais robusto.printf "\n" >> noeol-file
é ainda mais.csh
'secho
é a única conhecida a saída nada quando não passou qualquer argumento. Mas então, se formos apoiar conchas que não sejam do tipo Bourne, deveríamos fazê-lo emecho ''
vez deecho ""
comoecho ""
seria o resultado""<newline>
comrc
oues
por exemplo.tcsh
, ao contráriocsh
, imprime uma nova linha quando invocada sem argumentos - independentemente da configuração de$echo_style
.Outra solução usando
ed
. Esta solução afeta apenas a última linha e somente se\n
estiver ausente:Funciona essencialmente abrindo o arquivo para edição através de um script, o script é o único
w
comando que grava o arquivo de volta no disco. É baseado nesta frase encontrada naed(1)
página de manual:fonte
Uma maneira simples, portátil e compatível com POSIX de adicionar uma nova linha final ausente a um arquivo de texto seria:
Essa abordagem não precisa ler o arquivo inteiro; pode simplesmente procurar EOF e trabalhar a partir daí.
Essa abordagem também não precisa criar arquivos temporários nas suas costas (por exemplo, sed -i), para que os hardlinks não sejam afetados.
echo acrescenta uma nova linha ao arquivo somente quando o resultado da substituição do comando for uma sequência não vazia. Observe que isso só pode acontecer se o arquivo não estiver vazio e o último byte não for uma nova linha.
Se o último byte do arquivo for uma nova linha, tail retorna-o e a substituição de comando o remove; o resultado é uma sequência vazia. O teste -n falha e o eco não é executado.
Se o arquivo estiver vazio, o resultado da substituição do comando também será uma cadeia vazia e, novamente, o eco não será executado. Isso é desejável, porque um arquivo vazio não é um arquivo de texto inválido, nem é equivalente a um arquivo de texto não vazio com uma linha vazia.
fonte
yash
se o último caractere no arquivo for um caractere de vários bytes (em localidades UTF-8, por exemplo), ou se o local for C e o último byte no arquivo tiver o oitavo bit definido. Com outros shells (exceto zsh), ele não adicionaria uma nova linha se o arquivo terminasse em um byte NUL (mas, novamente, isso significaria que a entrada seria sem texto mesmo após a adição de uma nova linha).Adicione nova linha, independentemente:
Aqui está uma maneira de verificar se uma nova linha existe no final antes de adicionar uma, usando Python:
fonte
echo ""
parece mais robusto queecho -n '\n'
. Ou você pode usarprintf '\n'
A solução mais rápida é:
É muito rápido.
Em um arquivo de tamanho médio,
seq 99999999 >file
isso leva milissegundos.Outras soluções levam muito tempo:
Funciona em ash, bash, lksh, mksh, ksh93, attsh e zsh, mas não em yash.
Todas as outras soluções apresentadas aqui alteram o registro de data e hora do arquivo.
Se você precisar de uma solução portátil para yash (e todos os outros shells listados acima), ela poderá ser um pouco mais complexa:
fonte
A maneira mais rápida de testar se o último byte de um arquivo é uma nova linha é ler apenas esse último byte. Isso poderia ser feito
tail -c1 file
. No entanto, a maneira simplista de testar se o valor do byte é uma nova linha, dependendo da remoção usual do shell de uma nova linha à direita dentro de uma expansão de comando falha (por exemplo) em yash, quando o último caractere do arquivo é UTF- 8 valor.A maneira correta, compatível com POSIX, de todos os shells (razoáveis) para descobrir se o último byte de um arquivo é uma nova linha é usar xxd ou hexdump:
Em seguida, comparar a saída acima para
0A
fornecerá um teste robusto.É útil evitar adicionar uma nova linha a um arquivo vazio.
Arquivo que falhará em fornecer um último caractere
0A
, é claro:Curto e grosso. Isso leva muito pouco tempo, pois apenas lê o último byte (procure EOF). Não importa se o arquivo é grande. Em seguida, adicione apenas um byte, se necessário.
Nenhum arquivo temporário é necessário nem usado. Não há hardlinks afetados.
Se esse teste for executado duas vezes, ele não adicionará outra nova linha.
fonte
xxd
nemhexdump
os utilitários POSIX nem são. No baú da ferramenta POSIX, existeod -An -tx1
o valor hexadecimal de um byte.É melhor corrigir o editor do usuário que editou o arquivo pela última vez. Se você é a última pessoa a editar o arquivo - que editor você está usando, eu acho que ele tem um colega de texto ..?
fonte
emacs
não adiciona uma nova linha no final do arquivo.(setq require-final-newline 'ask)
na minha.emacs
Se você deseja adicionar rapidamente uma nova linha ao processar algum pipeline, use o seguinte:
também é compatível com POSIX.
Então, é claro, você pode redirecioná-lo para um arquivo.
fonte
cat file.csv | tr "\r" "\n" | { cat; echo; } | sed "/^[[:space:]]*$/d" | tail -n +2 | wc -l
Desde que não haja nulos na entrada:
... seria suficiente sempre acrescentar apenas uma nova linha ao final de um infile se ele ainda não tivesse um. E ele só precisa ler o arquivo de entrada uma vez para acertar.
fonte
paste infile 1<> infile
lugar.Embora não responda diretamente à pergunta, aqui está um script relacionado que escrevi para detectar arquivos que não terminam em nova linha. É muito rápido.
O script perl lê uma lista de nomes de arquivo (opcionalmente classificados) do stdin e para cada arquivo lê o último byte para determinar se o arquivo termina em uma nova linha ou não. É muito rápido porque evita a leitura de todo o conteúdo de cada arquivo. Ele gera uma linha para cada arquivo que lê, prefixado com "error:" se ocorrer algum tipo de erro, "empty:" se o arquivo estiver vazio (não termina com nova linha!), "EOL:" ("final de line ") se o arquivo terminar com nova linha e" no EOL: "se o arquivo não terminar com nova linha.
Nota: o script não trata nomes de arquivos que contêm novas linhas. Se você estiver em um sistema GNU ou BSD, poderá lidar com todos os nomes de arquivos possíveis adicionando -print0 para encontrar, -z para classificar e -0 para perl, assim:
Obviamente, você ainda precisará criar uma maneira de codificar os nomes dos arquivos com novas linhas na saída (deixadas como um exercício para o leitor).
A saída pode ser filtrada, se desejado, para acrescentar uma nova linha aos arquivos que não possuem uma, simplesmente com
A falta de uma nova linha final pode causar erros nos scripts, já que algumas versões do shell e outros utilitários não tratam adequadamente uma nova linha final ausente ao ler esse arquivo.
Na minha experiência, a falta de uma nova linha final é causada pelo uso de vários utilitários do Windows para editar arquivos. Eu nunca vi o vim causar uma nova linha final ausente ao editar um arquivo, embora ele relate esses arquivos.
Por fim, existem scripts muito mais curtos (mas mais lentos) que podem fazer um loop sobre as entradas de nome de arquivo para imprimir os arquivos que não terminam em nova linha, como:
fonte
Os editores
vi
/vim
/ sãoex
adicionados automaticamente<EOL>
ao EOF, a menos que o arquivo já o possua.Então tente:
que é equivalente a:
Testando:
Para corrigir vários arquivos, verifique: Como corrigir 'Nenhuma nova linha no final do arquivo' para muitos arquivos? na SO
Por que isso é tão importante? Para manter nossos arquivos compatíveis com POSIX .
fonte
Para aplicar a resposta aceita a todos os arquivos no diretório atual (mais subdiretórios):
Isso funciona no Linux (Ubuntu). No OS X, você provavelmente precisará usar
-i ''
(não testado).fonte
find .
lista todos os arquivos, incluindo os arquivos em.git
. Para excluir:find . -type f -not -path './.git/*' -exec sed -i -e '$a\' {} \;
Pelo menos nas versões GNU, simplesmente
grep ''
ouawk 1
canoniza sua entrada, adicionando uma nova linha final, se ainda não estiver presente. Eles copiam o arquivo no processo, o que leva tempo se for grande (mas a fonte não deve ser muito grande para ser lida de qualquer maneira?) E atualiza o modtime, a menos que você faça algo como(embora isso possa estar bem em um arquivo que você está fazendo check-in porque o modificou) e perde links físicos, permissões não padrão e ACLs, etc., a menos que você seja ainda mais cuidadoso.
fonte
grep '' file 1<> file
, apesar de ainda assim ler e gravar o arquivo completamente.Isso funciona no AIX ksh:
No meu caso, se o arquivo estiver faltando a nova linha, o
wc
comando retornará um valor de2
e escreveremos uma nova linha.fonte
Adicionando a resposta de Patrick Oscity , se você quiser aplicá-la a um diretório específico, também poderá usar:
find -type f | while read f; do tail -n1 $f | read -r _ || echo >> $f; done
Execute isso dentro do diretório ao qual você gostaria de adicionar novas linhas.
fonte
echo $'' >> <FILE_NAME>
adicionará uma linha em branco ao final do arquivo.echo $'\n\n' >> <FILE_NAME>
adicionará 3 linhas em branco ao final do arquivo.fonte
Se seu arquivo for finalizado com finais de linha do Windows
\r\n
e você estiver no Linux, poderá usar estesed
comando. Ele só será adicionado\r\n
à última linha se ainda não estiver lá:Explicação:
Se a última linha já contiver um
\r\n
, a regexp de pesquisa não corresponderá, portanto nada acontecerá.fonte
Você pode escrever um
fix-non-delimited-line
script como:Ao contrário de algumas das soluções fornecidas aqui,
Você pode usá-lo, por exemplo, como:
ou:
POSIX, você poderia fazer algo funcionalmente equivalente a
fonte