Najprostszą i najbardziej przenośną odpowiedzią jest uruchomienie tego:
#!/usr/bin/env perl
use strict;
use warnings;
use File::Find;
my @dirs = (@ARGV == 0) ? <*> : @ARGV;
find sub {
next unless -f && -T;
system('perl', '-i', '-pe', 's/[\t\xA0 ]+$//', $File::Find::name);
} => @dirs;
Wyjaśnię, dlaczego poniżej, gdzie również pokazuję, jak to zrobić za pomocą wiersza polecenia, a także jak radzić sobie z plikami tekstowymi trans-ASCII, takimi jak ISO-8859-1 (Latin-1) i UTF-8, które również nie mają -ASCII w nich białe znaki.
Reszta historii
Problem polega na tym, że find (1) nie obsługuje -T
operatora testowania plików, ani nie rozpoznaje kodowania, jeśli tak - co absolutnie musisz wykryć UTF-8, de facto standardowe kodowanie Unicode.
Co możesz zrobić, to uruchomić listę nazw plików przez warstwę, która wyrzuca pliki binarne. Na przykład
$ find . -type f | perl -nle 'print if -T' | xargs sed -i 's/[ \t]*$//'
Jednak teraz masz problem z białymi spacjami w nazwach plików, więc musisz opóźnić to z zerowym zakończeniem:
$ find . -type f -print0 | perl -0 -nle 'print if -T' | xargs -0 sed -i 's/[ \t]*$//'
Inną rzeczą, którą możesz zrobić, to nie używać, find
ale find2perl
ponieważ Perl -T
już rozumie :
$ find2perl * -type T -exec sed 's/[ \t]*$//' -i {} \; | perl
A jeśli chcesz, aby Perl założył, że jego pliki znajdują się w UTF-8, użyj
$ find2perl * -type T -exec sed 's/[ \t]*$//' -i {} \; | perl -CSD
Lub możesz zapisać wynikowy skrypt w pliku i edytować go. Naprawdę naprawdę nie powinieneś uruchamiać -T
testowania plików na żadnym starym pliku, ale tylko na tych, które są zwykłymi plikami, jak określa to po raz pierwszy -f
. W przeciwnym razie ryzykujesz otwarcie ofert specjalnych urządzeń, blokowanie na FIFO itp.
Jeśli jednak masz zamiar to zrobić, równie dobrze możesz całkowicie pominąć sed (1). Po pierwsze, jest bardziej przenośny, ponieważ wersja sed (1) POSIX nie rozumie -i
, podczas gdy wszystkie wersje Perla tak. Ostatnie wersje sed z miłością zawłaszczyły bardzo przydatną -i
opcję od Perla, w którym po raz pierwszy pojawia się ti.
Daje to również możliwość naprawy wyrażenia regularnego. Naprawdę powinieneś używać wzorca, który pasuje do jednej lub więcej końcowych białych spacji, a nie tylko ich zero, w przeciwnym razie będziesz działać wolniej z niepotrzebnego kopiowania. To jest to:
s/[ \t]*$//
Powinien być
s/[ \t]+$//
Jednak, jak uzyskać sed (1), aby zrozumieć, że wymaga rozszerzenia nie-POSIX, zwykle albo -R
dla Uniksów Systemu like, jak Solaris lub Linux, lub -E
dla BSD, takich jak OpenBSD lub MacOS. Podejrzewam, że jest to niemożliwe w systemie AIX. Wiesz, łatwiej jest napisać przenośną powłokę niż przenośny skrypt powłoki.
Ostrzeżenie o 0xA0
Chociaż są to jedyne poziome znaki białych znaków w ASCII, zarówno ISO-8859-1, a tym samym także Unicode, mają PRZESTRZEŃ BEZ PRZERWU w punkcie kodowym U + 00A0. Jest to jeden z dwóch najlepszych znaków spoza ASCII znalezionych w wielu korpusach Unicode, a ostatnio widziałem, jak wielu ludzi łamie kod wyrażenia regularnego, ponieważ o tym zapomnieli.
Dlaczego więc tego nie zrobisz:
$ find * -print0 | perl -0 -nle 'print if -f && -T' | xargs -0 perl -i -pe 's/[\t\xA0 ]+$//'
Jeśli możesz mieć UTF-8 pliki do czynienia z, dodać -CSD
, i jeśli używasz Perl v5.10 lub większy, można użyć \h
do poziomego spacji i \R
dla rodzajowego LINEBREAK, która obejmuje \r
, \n
, \r\n
, \f
, \cK
, \x{2028}
, i \x{2029}
:
$ find * -print0 | perl -0 -nle 'print if -f && -T' | xargs -0 perl -CSD -i -pe 's/\h+(?=\R*$)//'
Będzie to działać na wszystkich plikach UTF-8 bez względu na ich łamanie linii, eliminując końcowe białe znaki (właściwość znaku Unicode HorizSpace
), w tym nieprzyjemną PRZESTRZEŃ BEZ PRZERWU, która występuje przed łamaniem linii Unicode (włączając kombinacje CRLF) na końcu każdej linii.
Jest także o wiele bardziej przenośny niż wersja sed (1), ponieważ istnieje tylko jedna implementacja perla (1), ale wiele z sed (1).
Główny problem, jaki, jak widzę, nadal istnieje w przypadku find (1), ponieważ w niektórych naprawdę opornych systemach (wiesz, kim jesteś, AIX i Solaris) nie zrozumie -print0
dyrektywy nadkrytycznej . Jeśli taka jest Twoja sytuacja, powinieneś po prostu użyć File::Find
modułu bezpośrednio z Perla i nie używać żadnych innych narzędzi Uniksa. Oto czysta wersja kodu Perla, która nie polega na niczym innym:
#!/usr/bin/env perl
use strict;
use warnings;
use File::Find;
my @dirs = (@ARGV == 0) ? <*> : @ARGV;
find sub {
next unless -f && -T;
system('perl', '-i', '-pe', 's/[\t\xA0 ]+$//', $File::Find::name);
} => @dirs;
Jeśli korzystasz tylko z plików tekstowych ASCII lub ISO-8859-1, to dobrze, ale jeśli używasz plików ASCII lub UTF-8, dodaj -CSD
przełączniki w wywołaniu wewnętrznym Perla.
Jeśli masz mieszane kodowanie wszystkich trzech ASCII, ISO-8859-1 i UTF-8, obawiam się, że masz inny problem. :( Będziesz musiał ustalić kodowanie dla poszczególnych plików i nigdy nie ma dobrego sposobu, aby zgadnąć.
Biała spacja Unicode
Dla przypomnienia, Unicode ma 26 różnych białych znaków. Można skorzystać z unichars narzędzia wąchać te obecnie. Tylko pierwsze trzy poziome znaki białych znaków są prawie zawsze widoczne:
$ unichars '\h'
---- U+0009 CHARACTER TABULATION
---- U+0020 SPACE
---- U+00A0 NO-BREAK SPACE
---- U+1680 OGHAM SPACE MARK
---- U+180E MONGOLIAN VOWEL SEPARATOR
---- U+2000 EN QUAD
---- U+2001 EM QUAD
---- U+2002 EN SPACE
---- U+2003 EM SPACE
---- U+2004 THREE-PER-EM SPACE
---- U+2005 FOUR-PER-EM SPACE
---- U+2006 SIX-PER-EM SPACE
---- U+2007 FIGURE SPACE
---- U+2008 PUNCTUATION SPACE
---- U+2009 THIN SPACE
---- U+200A HAIR SPACE
---- U+202F NARROW NO-BREAK SPACE
---- U+205F MEDIUM MATHEMATICAL SPACE
---- U+3000 IDEOGRAPHIC SPACE
$ unichars '\v'
---- U+000A LINE FEED (LF)
---- U+000B LINE TABULATION
---- U+000C FORM FEED (FF)
---- U+000D CARRIAGE RETURN (CR)
---- U+0085 NEXT LINE (NEL)
---- U+2028 LINE SEPARATOR
---- U+2029 PARAGRAPH SEPARATOR