Używając systemów kontroli wersji denerwuje mnie hałas, gdy mówi diff No newline at end of file
.
Zastanawiałem się więc: jak dodać nowy wiersz na końcu pliku, aby pozbyć się tych wiadomości?
bash
shell
text-processing
newlines
k0pernikus
źródło
źródło
Odpowiedzi:
Aby rekurencyjnie zdezynfekować projekt, używam tego oneliner:
Wyjaśnienie:
git ls-files -z
wyświetla listę plików w repozytorium. Przyjmuje opcjonalny wzorzec jako dodatkowy parametr, który może być przydatny w niektórych przypadkach, jeśli chcesz ograniczyć operację do niektórych plików / katalogów. Alternatywnie, możesz użyćfind -print0 ...
lub podobnych programów do wyświetlenia listy plików, których dotyczy problem - po prostu upewnij się, że emitujeNUL
wpisy -delimitowane.while IFS= read -rd '' f; do ... done
iteruje wpisy, bezpiecznie obsługując nazwy plików, które zawierają białe znaki i / lub znaki nowej linii.tail -c1 < "$f"
odczytuje ostatni znak z pliku.read -r _
kończy z niezerowym statusem wyjścia, jeśli brakuje końcowego znaku nowej linii.|| echo >> "$f"
dołącza nowy wiersz do pliku, jeśli status wyjścia poprzedniego polecenia był niezerowy.źródło
find -name \*.java | while read f; do tail -n1 $f | read -r _ || echo >> $f; done
git ls-files
którego nadal będziesz oszczędzać przed edycją plików, które nie są śledzone w kontroli wersji.IFS=
do unset separator dobrze zachowuje otaczające białe znaki. Pozycje z zerowym zakończeniem są istotne tylko wtedy, gdy masz pliki lub katalogi z nową linią w nazwie, co wydaje się dość dalekosiężne, ale jest to bardziej poprawny sposób obsługi ogólnego przypadku, zgadzam się. Podobnie jak małe zastrzeżenie:-d
opcjaread
nie jest dostępna w POSIX sh.tail -n1 < "$f"
celu uniknięcia problemów z nazwami plików, które zaczynają się od-
(tail -n1 -- "$f"
nie działa dla pliku o nazwie-
). Możesz wyjaśnić, że odpowiedź jest teraz specyficzna dla zsh / bash.Proszę bardzo :
I alternatywnie dla OS X
sed
:Dodaje to
\n
na końcu pliku tylko wtedy, gdy nie kończy się on na nowej linii. Więc jeśli uruchomisz go dwa razy, nie doda kolejnej nowej linii:źródło
man sed
:$ Match the last line.
Ale może to działa tylko przypadkowo. Twoje rozwiązanie również działa.$
pasuje do ostatniego wiersza, dlaczego nie dodaje kolejnej nowej linii do ciągu, który już zawiera nową linię?$
. W wyrażeniu regularnym, na przykład w formularzu/<regex>/
, ma zwykłe znaczenie „dopasuj koniec linii”. W przeciwnym razie, użyty jako adres, sed nadaje mu specjalne znaczenie „ostatni wiersz w pliku”. Kod działa, ponieważ sed domyślnie dołącza nowy wiersz do jego wyniku, jeśli jeszcze go nie ma. Kod „$ a \” mówi tylko „dopasuj do ostatniego wiersza pliku i nic do niego nie dodawaj”. Ale domyślnie, sed dodaje nową linię do każdej linii, którą przetwarza (takiej jak ta$
linia), jeśli jeszcze jej nie ma./regex/
nadaje mu inne znaczenie. Strony podręcznika FreeBSD są nieco bardziej pouczające, jak sądzę: freebsd.org/cgi/man.cgi?query=sedSpójrz:
więc
echo "" >> noeol-file
powinien załatwić sprawę. (Czy chciałeś poprosić o zidentyfikowanie tych plików i ich naprawienie?)edit usunął
""
zecho "" >> foo
(patrz komentarz @ yuyichao) edit2 dodał""
ponownie ( ale zobacz komentarz @Keith Thompson)źródło
""
nie jest konieczne (przynajmniej dla bash) itail -1 | wc -l
mogą być wykorzystane, aby dowiedzieć się plik bez nowej linii na końcu""
jest to konieczne do bash, ale widziałemecho
implementacje, które nic nie drukują, gdy są wywoływane bez argumentów (chociaż żaden z tych, które mogę teraz znaleźć, nie robi tego).echo "" >> noeol-file
jest prawdopodobnie nieco bardziej wytrzymały.printf "\n" >> noeol-file
jest jeszcze bardziej.csh
jestecho
to jeden znany wyjściowym nic, gdy nie przeszedł żadnego argumentu. Ale jeśli mamy zamiar wspierać non-Bourne'a jak muszle, powinniśmy zrobić toecho ''
zamiastecho ""
jakecho ""
byłoby Ouput""<newline>
zrc
lubes
na przykład.tcsh
, w przeciwieństwie docsh
, drukuje nowy wiersz, gdy jest wywoływany bez argumentów - niezależnie od ustawienia$echo_style
.Inne rozwiązanie przy użyciu
ed
. To rozwiązanie wpływa tylko na ostatni wiersz i tylko w przypadku\n
jego braku:Zasadniczo działa otwieranie pliku do edycji za pomocą skryptu, skrypt jest pojedynczą
w
komendą, która zapisuje plik z powrotem na dysk. Opiera się na tym zdaniu znalezionym naed(1)
stronie podręcznika:źródło
Prostym, przenośnym, zgodnym z POSIX sposobem dodania nieobecnej, końcowej nowej linii do pliku tekstowego byłoby:
To podejście nie musi czytać całego pliku; może po prostu szukać EOF i stamtąd pracować.
Podejście to nie musi również tworzyć plików tymczasowych za twoimi plecami (np. Sed -i), więc nie ma to wpływu na hardlink.
echo dołącza nowy wiersz do pliku tylko wtedy, gdy wynikiem podstawienia polecenia jest niepusty ciąg. Zauważ, że może się to zdarzyć tylko wtedy, gdy plik nie jest pusty, a ostatni bajt nie jest znakiem nowej linii.
Jeśli ostatnim bajtem pliku jest nowa linia, tail ją zwraca, a następnie zastępuje ją rozkaz; wynikiem jest pusty ciąg. Test -n kończy się niepowodzeniem i echo nie działa.
Jeśli plik jest pusty, wynikiem podstawienia polecenia jest również pusty ciąg znaków i ponownie echo nie działa. Jest to pożądane, ponieważ pusty plik nie jest niepoprawnym plikiem tekstowym, ani nie jest równoważny niepustemu plikowi tekstowemu z pustą linią.
źródło
yash
jeśli ostatni znak w pliku jest znakiem wielobajtowym (na przykład w ustawieniach narodowych UTF-8), lub jeśli ustawienia narodowe to C, a ostatni bajt w pliku ma ustawiony 8. bit. W przypadku innych powłok (oprócz zsh) nie dodawałby nowego wiersza, jeśli plik kończy się bajtem NUL (ale z drugiej strony oznaczałoby to, że dane wejściowe byłyby nietekstowe nawet po dodaniu nowego wiersza).Dodaj nową linię niezależnie od:
Oto sposób sprawdzenia, czy nowa linia istnieje na końcu przed jej dodaniem, za pomocą Pythona:
źródło
echo ""
wydaje się bardziej solidny niżecho -n '\n'
. Lub możesz użyćprintf '\n'
Najszybszym rozwiązaniem jest:
Jest naprawdę szybki.
W przypadku pliku średniej wielkości
seq 99999999 >file
zajmuje to milisekundy.Inne rozwiązania zajmują dużo czasu:
Działa w ash, bash, lksh, mksh, ksh93, attsh i zsh, ale nie yash.
Wszystkie inne przedstawione tutaj rozwiązania zmieniają znacznik czasu pliku.
Jeśli potrzebujesz rozwiązania przenośnego do yash (i wszystkich innych powłok wymienionych powyżej), może być nieco bardziej skomplikowane:
źródło
Najszybszym sposobem sprawdzenia, czy ostatnim bajtem pliku jest nowa linia, jest odczyt tylko tego ostatniego bajtu. Można to zrobić
tail -c1 file
. Jednak uproszczony sposób sprawdzenia, czy wartość bajtu jest nową linią, w zależności od powłoki zwykle usuwanej końcowej nowej linii w rozszerzeniu komendy kończy się niepowodzeniem (na przykład) w yash, gdy ostatnim znakiem w pliku jest UTF- 8 wartości.Prawidłowym, zgodnym z POSIX, wszystkim (rozsądnym) sposobem na sprawdzenie, czy ostatnim bajtem pliku jest nowa linia, jest użycie xxd lub hexdump:
Następnie porównanie wyniku powyższego z
0A
zapewni dokładny test.Przydatne jest unikanie dodawania nowej linii do inaczej pustego pliku.
Plik, który nie zapewni ostatniego znaku
0A
, oczywiście:Krótkie i słodkie. Zajmuje to bardzo mało czasu, ponieważ po prostu odczytuje ostatni bajt (szukaj do EOF). Nie ma znaczenia, czy plik jest duży. W razie potrzeby dodaj tylko jeden bajt.
Pliki tymczasowe nie są potrzebne ani używane. Nie dotyczy to linków twardych.
Jeśli ten test zostanie uruchomiony dwukrotnie, nie doda on nowej linii.
źródło
xxd
anihexdump
narzędzia POSIX. W zestawie narzędzi POSIX jestod -An -tx1
wartość szesnastkowa bajtu.Lepiej popraw edytor użytkownika, który ostatnio edytował plik. Jeśli jesteś ostatnią osobą, która edytowała plik - jakiego edytora używasz, domyślam się, że jest to partner tekstowy?
źródło
emacs
nie dodać nową linię na końcu pliku.(setq require-final-newline 'ask)
w swoim.emacs
Jeśli chcesz tylko szybko dodać nowy wiersz podczas przetwarzania potoku, użyj tego:
jest również zgodny z POSIX.
Następnie możesz oczywiście przekierować go do pliku.
źródło
cat file.csv | tr "\r" "\n" | { cat; echo; } | sed "/^[[:space:]]*$/d" | tail -n +2 | wc -l
Pod warunkiem, że na wejściu nie ma wartości null:
... wystarczyłoby zawsze dodawać nową linię do końca infilu, jeśli jeszcze go nie miał. I musi tylko odczytać plik wejściowy za jednym razem, aby go poprawnie.
źródło
paste infile 1<> infile
.Chociaż nie odpowiada bezpośrednio na pytanie, oto pokrewny skrypt, który napisałem, aby wykryć pliki, które nie kończą się na nowej linii. To jest bardzo szybkie.
Skrypt perla czyta listę (opcjonalnie posortowanych) nazw plików ze standardowego wejścia i dla każdego pliku odczytuje ostatni bajt, aby ustalić, czy plik kończy się na nowej linii, czy nie. Jest bardzo szybki, ponieważ unika czytania całej zawartości każdego pliku. Wyprowadza jeden wiersz dla każdego pliku, który czyta, z prefiksem „error:”, jeśli wystąpi jakiś błąd, „empty:”, jeśli plik jest pusty (nie kończy się na nowej linii!), „EOL:” („end of linia ”), jeśli plik kończy się na nowej linii i„ bez EOL: ”, jeśli plik nie kończy się na nowej linii.
Uwaga: skrypt nie obsługuje nazw plików zawierających znaki nowej linii. Jeśli korzystasz z systemu GNU lub BSD, możesz obsłużyć wszystkie możliwe nazwy plików, dodając -print0 do znalezienia, -z do sortowania i -0 do perla, w następujący sposób:
Oczywiście nadal musisz wymyślić sposób kodowania nazw plików z nowymi liniami w danych wyjściowych (pozostawionych jako ćwiczenie dla czytelnika).
W razie potrzeby dane wyjściowe można przefiltrować, aby dodać nowy wiersz do plików, które go nie mają, najprościej mówiąc
Brak końcowej nowej linii może powodować błędy w skryptach, ponieważ niektóre wersje powłoki i innych narzędzi nie będą poprawnie obsługiwały brakującej nowej linii podczas czytania takiego pliku.
Z mojego doświadczenia wynika, że brak ostatecznej nowej linii jest spowodowany używaniem różnych narzędzi systemu Windows do edycji plików. Nigdy nie widziałem, aby vim powodował brak końcowej nowej linii podczas edycji pliku, chociaż będzie raportował o takich plikach.
Wreszcie, istnieją znacznie krótsze (ale wolniejsze) skrypty, które mogą zapętlać wprowadzane nazwy plików, aby wydrukować te pliki, które nie kończą się na nowej linii, takie jak:
źródło
W
vi
/vim
/ex
redaktorzy automatycznie dodać<EOL>
na EOF chyba że plik ma go już.Więc spróbuj albo:
co jest równoważne z:
Testowanie:
Aby poprawić wiele plików, zaznacz: Jak naprawić „Brak nowej linii na końcu pliku” dla wielu plików? w SO
Dlaczego to takie ważne? Aby zachować zgodność naszych plików z POSIX .
źródło
Aby zastosować zaakceptowaną odpowiedź do wszystkich plików w bieżącym katalogu (plus podkatalogach):
Działa to w systemie Linux (Ubuntu). W systemie OS X prawdopodobnie musisz użyć
-i ''
(niesprawdzony).źródło
find .
zawiera listę wszystkich plików, w tym plików w.git
. Aby wykluczyć:find . -type f -not -path './.git/*' -exec sed -i -e '$a\' {} \;
Przynajmniej w wersjach GNU po prostu
grep ''
lubawk 1
kanonizuje jego wejście, dodając końcowy nowy wiersz, jeśli jeszcze nie jest obecny. Kopiują plik w tym procesie, co zajmuje dużo czasu (ale źródło nie powinno być zbyt duże, aby odczytać?) I aktualizuje modtime, chyba że zrobisz coś takiego(chociaż może to być w porządku w przypadku pliku, który się rejestrujesz, ponieważ go zmodyfikowałeś) i traci on linki dowiązania, uprawnienia domyślne i listy ACL itp., chyba że jesteś jeszcze bardziej ostrożny.
źródło
grep '' file 1<> file
, ale nadal czyta i zapisuje plik w pełni.Działa to w systemie AIX ksh:
W moim przypadku, jeśli w pliku brakuje nowej linii,
wc
polecenie zwraca wartość2
i piszemy nową linię.źródło
Dodając do odpowiedzi Patricka Oscity , jeśli chcesz po prostu zastosować ją do określonego katalogu, możesz również użyć:
find -type f | while read f; do tail -n1 $f | read -r _ || echo >> $f; done
Uruchom to w katalogu, do którego chcesz dodać nowe wiersze.
źródło
echo $'' >> <FILE_NAME>
doda pusty wiersz na końcu pliku.echo $'\n\n' >> <FILE_NAME>
doda 3 puste linie na końcu pliku.źródło
Jeśli plik jest zakończony zakończeniami linii systemu Windows
\r\n
i jesteś w systemie Linux, możesz użyć tegosed
polecenia. Dodaje tylko\r\n
do ostatniego wiersza, jeśli jeszcze go nie ma:Wyjaśnienie:
Jeśli ostatni wiersz zawiera już
\r\n
wyrażenie, wyrażenie regularne wyszukiwania nie będzie pasować, dlatego nic się nie wydarzy.źródło
Możesz napisać
fix-non-delimited-line
skrypt taki jak:W przeciwieństwie do niektórych podanych tutaj rozwiązań, to
Możesz go użyć na przykład jako:
lub:
POSIXly, możesz zrobić coś funkcjonalnie równoważnego
źródło