it-swarm-eu.dev

Jak přidat nový řádek na konec souboru?

Pomocí systémů pro správu verzí se naštvám, když rozdíl říká No newline at end of file.

Tak jsem přemýšlel: Jak přidat nový řádek na konec souboru, jak se těchto zpráv zbavit?

208
k0pernikus

K rekurzivní dezinfekci projektu používám tento oneliner:

git ls-files -z | while IFS= read -rd '' f; do tail -c1 < "$f" | read -r _ || echo >> "$f"; done

Vysvětlení:

  • git ls-files -z vypíše soubory v úložišti. Jako doplňkový parametr je volitelný vzor, ​​který může být v některých případech užitečný, pokud chcete omezit provoz na určité soubory/adresáře. Jako alternativu můžete použít find -print0 ... nebo podobné programy pro výpis postižených souborů - stačí se ujistit, že vydává NUL - oddělené položky.

  • while IFS= read -rd '' f; do ... done opakuje položky, bezpečně manipuluje se jmény souborů, které obsahují mezery a/nebo nové řádky.

  • tail -c1 < "$f" přečte poslední znak ze souboru.

  • read -r _ ukončí se stavem nenulového ukončení, pokud chybí koncové nové řádky.

  • || echo >> "$f" připojí k souboru nový řádek, pokud byl výstupní stav předchozího příkazu nenulový.

46
Patrick Oscity

Tady to máte :

sed -i -e '$a\' file

A alternativně pro OS X sed:

sed -i '' -e '$a\' file

Tím se přidá \n na konec souboru pouze , pokud ještě nekončí novým řádkem. Pokud ji tedy spustíte dvakrát, nepřidá další nový řádek:

$ cd "$(mktemp -d)"
$ printf foo > test.txt
$ sed -e '$a\' test.txt > test-with-eol.txt
$ diff test*
1c1
< foo
\ No newline at end of file
---
> foo
$ echo $?
1
$ sed -e '$a\' test-with-eol.txt > test-still-with-one-eol.txt
$ diff test-with-eol.txt test-still-with-one-eol.txt
$ echo $?
0
215
l0b0

Podívej se:

$ echo -n foo > foo 
$ cat foo
foo$
$ echo "" >> foo
$ cat foo
foo

tak echo "" >> noeol-file by měl udělat trik. (Nebo jste chtěli požádat o identifikaci těchto souborů a jejich opravu?)

pravit odstranil "" z echo "" >> foo (viz komentář @ yuyichao) edit2 přidal "" znovu (, ale viz komentář @ Kitha Thompsona)

41
sr_

Další řešení pomocí ed. Toto řešení ovlivní pouze poslední řádek a pouze v případě, že chybí \n:

ed -s file <<< w

V zásadě funguje otevření souboru pro úpravy skriptem, skript je jediný příkaz w, který soubor zapíše zpět na disk. Je založena na této větě nalezené na stránce ed(1) man:

 OMEZENÍ 
 (...) 
 
 Pokud není textový (nebinární) soubor ukončen znakem nového řádku, 
 Pak ed připojí jednu při čtení/zápisu. V případě binárního 
 Souboru ed nepřidává nový řádek při čtení/zápisu. 
16
enzotib

Přidat nový řádek bez ohledu na to:

echo >> filename

Zde je způsob, jak zkontrolovat, zda existuje nový řádek na konci před přidáním jednoho, pomocí Pythonu:

f=filename; python -c "import sys; sys.exit(open(\"$f\").read().endswith('\n'))" && echo >> $f
15
Alexander

Jednoduchý, přenosný, kompatibilní s POSIXem způsob, jak přidat chybějící konečný nový řádek do textového souboru:

[ -n "$(tail -c1 file)" ] && echo >> file

Tento přístup nemusí číst celý soubor; může se jednoduše snažit EOF) a odtud pracovat.

Tento přístup také nemusí vytvářet dočasné soubory za zády (např. Sed -i), takže pevné odkazy nejsou ovlivněny.

echo k souboru přidá nový řádek, pouze pokud je výsledkem nahrazení příkazu neprázdný řetězec. Všimněte si, že k tomu může dojít, pouze pokud soubor není prázdný a poslední bajt není nový řádek.

Pokud je poslední bajt souboru nový řádek, vrátí jej tail, poté jej odstraní příkazový substituce; výsledkem je prázdný řetězec. Test -n selhal a echo se nespustí.

Pokud je soubor prázdný, výsledkem substituce příkazu je také prázdný řetězec a echo se znovu nespustí. To je žádoucí, protože prázdný soubor není neplatný textový soubor, ani není ekvivalentní neprázdnému textovému souboru s prázdným řádkem.

12
Barefoot IO

Nejrychlejším řešením je:

[ -n "$(tail -c1 file)" ] && printf '\n' >>file 

  1. Je opravdu rychlý.
    Na soubor střední velikosti seq 99999999 >file to trvá milisekundy.
    Jiná řešení trvat dlouho:

    [ -n "$(tail -c1 file)" ] && printf '\n' >>file  0.013 sec
    vi -ecwq file                                    2.544 sec
    paste file 1<> file                             31.943 sec
    ed -s file <<< w                             1m  4.422 sec
    sed -i -e '$a\' file                         3m 20.931 sec
    
  2. Funguje v popelu, bashu, lksh, mksh, ksh93, attsh a zsh, ale ne jash.

  3. Nemění časové razítko souboru, pokud není třeba přidávat nový řádek.
    Všechna zde uvedená řešení mění časovou značku souboru.
  4. Všechna výše uvedená řešení jsou platná POSIX.

Pokud potřebujete řešení přenosné do yash (a všech ostatních výše uvedených skořápek), může to být trochu složitější:

f=file
if       [ "$(tail -c1 "$f"; echo x)" != "$(printf '\nx')" ]
then     printf '\n' >>"$f"
fi
8
Isaac

Nejrychlejším způsobem, jak otestovat, zda je poslední bajt souboru nový řádek, je přečíst pouze poslední bajt. To lze provést pomocí tail -c1 file. Zjednodušený způsob, jak otestovat, zda je hodnota bajtu nový řádek, se však v závislosti na obvyklém odstranění koncového nového řádku v prostředí Shell při rozšíření příkazu nezdaří (například) v yash, když posledním znakem v souboru je UTF- 8 hodnota.

Správný, všechny (rozumné) skořápky kompatibilní s POSIXem, jak zjistit, zda je poslední řádek souboru nový řádek, je použít buď xxd, nebo hexdump:

tail -c1 file | xxd -u -p
tail -c1 file | hexdump -v -e '/1 "%02X"'

Porovnáním výše uvedeného výstupu s 0A poskytne robustní test.
Je užitečné vyhnout se přidání nového řádku do jinak prázdného souboru.
Soubor, který neposkytne poslední znak 0A, samozřejmě:

f=file
a=$(tail -c1 "$f" | hexdump -v -e '/1 "%02X"')
[ -s "$f" -a "$a" != "0A" ] && echo >> "$f"

Krátký a sladký. To zabere jen velmi málo času, protože to jen přečte poslední bajt (hledat EOF). Nezáleží na tom, zda je soubor velký. Pak přidejte pouze jeden bajt, pokud je to potřeba.

Nepotřebujete ani nepoužíváte žádné dočasné soubory. Nejsou ovlivněny žádné pevné odkazy.

Pokud je tento test spuštěn dvakrát, ne přidá další nový řádek.

7
sorontar

Je lepší opravit správce uživatele, který soubor naposledy upravil. Pokud jste poslední, kdo soubor upravil - jaký editor používáte, hádám textmate ..?

4
AD7six

Pokud nejsou na vstupu žádné nuly:

paste - <>infile >&0

... stačilo by vždy připojit nový řádek k zadnímu konci infiltu, pokud by jej ještě neměl. A je třeba jen přečíst vstupní soubor najednou, aby to bylo správné.

3
user157018

Pokud chcete při zpracování nějakého potrubí rychle přidat nový řádek, použijte toto:

outputting_program | { cat ; echo ; }

je také kompatibilní s POSIXem.

Potom ji samozřejmě můžete přesměrovat do souboru.

3
MichalH

Ačkoli to přímo neodpovídá na otázku, zde je související skript, který jsem napsal, aby detekoval soubory, které nekončí na novém řádku. Je to velmi rychlé.

find . -type f | # sort |        # sort file names if you like
/usr/bin/Perl -lne '
   open FH, "<", $_ or do { print " error: $_"; next };
   $pos = sysseek FH, 0, 2;                     # seek to EOF
   if (!defined $pos)     { print " error: $_"; next }
   if ($pos == 0)         { print " empty: $_"; next }
   $pos = sysseek FH, -1, 1;                    # seek to last char
   if (!defined $pos)     { print " error: $_"; next }
   $cnt = sysread FH, $c, 1;
   if (!$cnt)             { print " error: $_"; next }
   if ($c eq "\n")        { print "   EOL: $_"; next }
   else                   { print "no EOL: $_"; next }
'

Skript Perl přečte seznam (volitelně seřazených) názvů souborů ze stdin a pro každý soubor přečte poslední bajt, aby určil, zda soubor končí na novém řádku nebo ne. Je to velmi rychlé, protože zabraňuje čtení celého obsahu každého souboru. Na každý soubor, který přečte, vydá jeden řádek, předponou "chyba:" pokud dojde k nějaké chybě, "prázdný:" pokud je soubor prázdný (nekončí novým řádkem!), "EOL:" ("konec řádek "), pokud soubor končí řádkem newline a" no EOL: ", pokud soubor nekončí řádkem newline.

Poznámka: skript nezpracovává názvy souborů, které obsahují nové řádky. Pokud používáte systém GNU nebo BSD), můžete zpracovat všechny možné názvy souborů přidáním -print0 k nalezení, -z k třídění a -0 k Perlu, jako je toto:

find . -type f -print0 | sort -z |
/usr/bin/Perl -ln0e '
   open FH, "<", $_ or do { print " error: $_"; next };
   $pos = sysseek FH, 0, 2;                     # seek to EOF
   if (!defined $pos)     { print " error: $_"; next }
   if ($pos == 0)         { print " empty: $_"; next }
   $pos = sysseek FH, -1, 1;                    # seek to last char
   if (!defined $pos)     { print " error: $_"; next }
   $cnt = sysread FH, $c, 1;
   if (!$cnt)             { print " error: $_"; next }
   if ($c eq "\n")        { print "   EOL: $_"; next }
   else                   { print "no EOL: $_"; next }
'

Samozřejmě byste stále museli vymyslet způsob kódování názvů souborů s novými řádky ve výstupu (ponecháno jako cvičení pro čtenáře).

Výstup by mohl být filtrován, pokud je to žádoucí, aby přidal nový řádek k souborům, které jej nemají, nejjednodušším způsobem

 echo >> "$filename"

Nedostatek konečného nového řádku může způsobit chyby ve skriptech, protože některé verze prostředí Shell a další obslužné programy nebudou správně číst chybějící konečný nový řádek při čtení takového souboru.

Podle mých zkušeností je chybějící poslední řádek způsoben použitím různých nástrojů systému Windows k úpravě souborů. Nikdy jsem neviděl, že vim způsobuje chybějící poslední řádek při úpravách souboru, ačkoli bude o takových souborech informovat.

Konečně, tam jsou mnohem kratší (ale pomalejší) skripty, které mohou opakovat jejich vstupy pro název souboru pro tisk těch souborů, které nekončí na novém řádku, jako například:

/usr/bin/Perl -ne 'print "$ARGV\n" if /.\z/' -- FILE1 FILE2 ...

Editori vi/vim/ex editoři automaticky přidají <EOL> at EOF), pokud jej soubor již nemá.

Zkuste také:

vi -ecwq foo.txt

což odpovídá:

ex -cwq foo.txt

Testování:

$ printf foo > foo.txt && wc foo.txt
0 1 3 foo.txt
$ ex -scwq foo.txt && wc foo.txt
1 1 4 foo.txt

Chcete-li opravit více souborů, zkontrolujte: Jak opravit 'Žádný nový řádek na konci souboru' pro mnoho souborů? na SO

Proč je to tak důležité? Zachovat naše soubory kompatibilní s POSIX .

1
kenorb

Pokud je váš soubor ukončen Windows zakončení řádku \r\n a jste v Linuxu, můžete použít tento příkaz sed. Přidá pouze \r\n na poslední řádek, pokud tam ještě není:

sed -i -e '$s/\([^\r]\)$/\1\r\n/'

Vysvětlení:

-i    replace in place
-e    script to run
$     matches last line of a file
s     substitute
\([^\r]\)$    search the last character in the line which is not a \r
\1\r\n    replace it with itself and add \r\n

Pokud poslední řádek již obsahuje \r\n pak se regexp vyhledávání neshoduje, proto se nic nestane.

0
masgo

Chcete-li použít přijatou odpověď na všechny soubory v aktuálním adresáři (plus podadresáře):

$ find . -type f -exec sed -i -e '$a\' {} \;

Toto funguje na Linuxu (Ubuntu). V OS X budete pravděpodobně muset použít -i '' (netestováno).

0
friederbluemle

Mohli byste napsat fix-non-delimited-line skript jako:

#! /bin/zsh -
zmodload zsh/system || exit
ret=0
for file do
  (){
    sysseek -w end -1 || {
      syserror -p "Can't seek before the last byte: "
      return 1
    }
    read -r x || print -u0
  } <> $file || ret=$?
done
exit $ret

Na rozdíl od některých zde uvedených řešení

  • měl by být účinný v tom, že nezvětší žádný proces, přečte pouze jeden bajt pro každý soubor a nepřepíše jej znovu (pouze připojí nový řádek)
  • nepřeruší symlinks/hardlinks ani neovlivní metadata (také, ctime/mtime jsou aktualizovány pouze při přidání nového řádku)
  • by mělo fungovat dobře, i když poslední bajt je NUL nebo je součástí vícebajtového znaku.
  • by mělo fungovat v pořádku bez ohledu na to, jaké znaky nebo jiné znaky mohou obsahovat názvy souborů
  • Měl by zpracovat správně nečitelné nebo nepoužitelné nebo neuznatelné soubory (a podle toho nahlásit chyby)
  • Nemělo by se přidat nový řádek k prázdným souborům (ale v tomto případě nahlásí chybu o neplatném hledání)

Můžete jej použít například jako:

that-script *.txt

nebo:

git ls-files -z | xargs -0 that-script
0

Přidáte-li odpověď Patricka Oscity , pokud ji chcete použít pouze na konkrétní adresář, můžete také použít:

find -type f | while read f; do tail -n1 $f | read -r _ || echo >> $f; done

Spusťte to uvnitř adresáře, do kterého chcete přidat nové řádky.

0
cipher

echo $'' >> <FILE_NAME> přidá na konec souboru prázdný řádek.

echo $'\n\n' >> <FILE_NAME> přidá 3 prázdné řádky na konec souboru.

0
user247137

Alespoň ve verzích GNU, jednoduše grep '' nebo awk 1 kanonikalizuje jeho vstup a přidává poslední nový řádek, pokud již není přítomen. Zkopírují soubor do procesu, což zabere čas, pokud je velký (ale zdroj by neměl být příliš velký na to, aby byl přečten?) A aktualizuje modtime, pokud neuděláte něco jako

 mv file old; grep '' <old >file; touch -r old file

(i když to může být v pořádku u souboru, který se přihlašujete, protože jste jej upravili), a ztratí hardwarové odkazy, neoficiální oprávnění a ACL atd., pokud nebudete ještě opatrnější.

0
dave_thompson_085

Spousta skvělých návrhů, ale jedním z nápadů by bylo odstranit nový řádek a přidat jeden, abyste věděli, že je nepřetržitě nepřidáváte:

Vezměte soubor "foo":

Odebrat nový řádek, pokud existuje:

  truncate -s $(($(stat -c '%s' foo)-1)) foo

Pak přidejte jednu:

  sed -i -e '$a\' foo

Proto foo bude vždy obsahovat alespoň jeden nový řádek.

nebo sledujte soubor a hledejte nový řádek, pokud jej neobsahuje.

  grep -q "[^0-9a-z-]" <<< $(tail -1 ./foo) && echo " " >> ./foo
0
Mike Q

Toto funguje v AIX ksh:

lastchar=`tail -c 1 *filename*`
if [ `echo "$lastchar" | wc -c` -gt "1" ]
then
    echo "/n" >> *filename*
fi

V mém případě, pokud v souboru chybí nový řádek, příkaz wc vrátí hodnotu 2 a píšeme nový řádek.

0
Daemoncan