Mam pliki w podkatalogach bieżącego katalogu, które mogą, ale nie muszą mieć nowych linii na końcu; jak mogę znaleźć pliki, które nie mają nowej linii na końcu?
Próbowałem tego:
find . -name '*.styl' | while read file; do
awk 'END{print}' $file | grep -E '^$' > /dev/null || echo $file;
done
ale to nie działa. awk 'END{print}' $file
wypisuje linię przed pustą nową linią, taką samą jak tail -n 1 $file
.
awk 'END{print}' $file
: ignoruje to całkowicie zawartość pliku $, a po zakończeniu analizy wszystkich plików zawartych w pliku „$ plik” dodaje nowy wiersz. Ponieważ jest to jedyna rzecz, którą wypisuje polecenie awk, można go zastąpić:printf '\n'
(bez żadnego mentino pliku $) i zrobić to samo. Myślę, że to nie było to, do czego dążyłeś (tj. Wydrukować ostatni wiersz pliku?)c
podobnie jak FreeBSD, ale nie zauważyłem, że jest to udokumentowane jako zależne od implementacji: gnu.org/software/gawk/manual/… . Więc to nie zdarzy, ale nie zawsze.Odpowiedzi:
Aby wyjaśnić,
\n
znak LF (aka lub znak nowej linii) jest separatorem linii , a nie separatorem linii. Linia nie jest zakończona, chyba że zostanie zakończona znakiem nowej linii. Plik, który zawiera tylkoa\nb
nie jest poprawnym plikiem tekstowym, ponieważ zawiera znaki po ostatnim wierszu. To samo dotyczy pliku, który zawiera tylkoa
. Plika\n
zawierający jedną niepustą linię.Tak więc plik, który kończy się co najmniej jedną pustą linią, kończy się dwoma znakami nowej linii lub zawiera pojedynczy znak nowej linii.
Gdyby:
Dane wyjściowe
\n
lub\n \n
plik zawiera co najmniej jedną końcową pustą linię. Jeśli nic nie wypisuje, to jest to pusty plik, jeśli wypisuje<anything-but-\0> \n
, to kończy się niepustym wierszem. Cokolwiek innego, to nie jest plik tekstowy.Teraz, aby użyć tego do znalezienia plików, które kończą się pustą linią, OK, jest to wydajne (szczególnie w przypadku dużych plików), ponieważ odczytuje tylko dwa ostatnie bajty plików, ale najpierw dane wyjściowe nie są łatwe do analizy programowej, szczególnie biorąc pod uwagę, że są niespójne między
od
kolejnymi implementacjami i musielibyśmy uruchomić jedentail
i jedenod
na plik.(aby znaleźć pliki kończące się pustą linią) uruchomiłoby jak najmniej poleceń, ale oznaczałoby odczytanie pełnej zawartości wszystkich plików.
Najlepiej byłoby, gdybyś potrzebował powłoki, która może sama odczytać koniec pliku.
Z
zsh
:źródło
are_textfiles () { nontext=0; rem="return 0 if all args are files with terminating newline, or n [=number of non-textfiles]" ; for f in "$@" ; do [ -f "$f" ] && { tail -c 1 "$f" | od -An -vtc | grep "\\n" ;} >/dev/null 2>&1 || ((nontext++)) ; done ; return $nontext ; }
. Użyj jako:if ( are_textfiles this that otherthing ) ; then echo all are text files ; else echo "are_textfiles returned : $?" ; fi
Z
gnu sed
i powłoką jakzsh
(lubbash
zshopt -s globstar
):sprawdza to, czy ostatni wiersz każdego pliku nie jest pusty, jeśli tak, to drukuje nazwę pliku.
Jeśli chcesz coś przeciwnego (wydrukować nazwy plików, jeśli ostatnia linia jest pusta) po prostu zastąpić
/./
z/^$/
źródło
-s
w akcji. Dziękuję GNU!Prawidłowo zakończony plik tekstowy z pustym ostatnim wierszem kończy się na dwa
\n
.Zatem oczekujemy, że to
tail -c2
musi być równe$'\n\n'
.Niestety rozszerzenia poleceń usuwają końcowe nowe wiersze. Będziemy potrzebować trochę ulepszeń.
Możemy nawet trochę rozwinąć, aby sprawdzić, które pliki nie mają końca nowej linii:
Zauważ, że nowa linia może być zmieniona na coś podobnego w
$'\r\n
razie potrzeby.W takim przypadku zmień także
tail -c2
natail -c4
.źródło
źródło
cat $file 2>&1 /dev/null
, lub jeśli jest to tylko Bash,cat $file &> /dev/null
.$file
wszędzie tam, gdzie jest używany - i$(commands ...)
zamiast tego używaj zamiast`backticks`
...