La risposta più semplice e portatile è eseguire questo:
#!/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;
Spiego perché di seguito, dove mostro anche come farlo utilizzando solo la riga di comando, nonché come gestire file di testo trans-ASCII come ISO-8859-1 (Latin-1) e UTF-8, che spesso non hanno -ASCII spazi bianchi in essi.
Il resto della storia
Il problema è che find (1) non supporta l' -T
operatore filetest, né riconosce le codifiche in caso affermativo, che è assolutamente necessario rilevare UTF-8, codifica Unicode di fatto standard.
Quello che potresti fare è eseguire l'elenco dei nomi di file attraverso un livello che elimina i file binari. Per esempio
$ find . -type f | perl -nle 'print if -T' | xargs sed -i 's/[ \t]*$//'
Tuttavia ora hai problemi con gli spazi bianchi nei nomi dei file, quindi devi ritardare con una terminazione nulla:
$ find . -type f -print0 | perl -0 -nle 'print if -T' | xargs -0 sed -i 's/[ \t]*$//'
Un'altra cosa che potresti fare è usare non find
ma find2perl
, poiché Perl comprende -T
già:
$ find2perl * -type T -exec sed 's/[ \t]*$//' -i {} \; | perl
E se vuoi che Perl presuma che i suoi file siano in UTF-8, usa
$ find2perl * -type T -exec sed 's/[ \t]*$//' -i {} \; | perl -CSD
Oppure potresti salvare lo script risultante in un file e modificarlo. Dovresti davvero non solo eseguire il -T
filetest su qualsiasi vecchio file, ma piuttosto solo su quelli che sono file semplici come da prima determinato da -f
. Altrimenti rischi di aprire speciali dispositivi, bloccare su Fifos, ecc.
Tuttavia, se hai intenzione di fare tutto ciò, potresti anche saltare sed (1) del tutto. Per prima cosa, è più portatile, poiché la versione POSIX di sed (1) non capisce -i
, mentre tutte le versioni di Perl lo fanno. Le versioni degli ultimi giorni di sed si sono appropriate amorevolmente -i
dell'opzione molto utile di Perl dove appare per la prima volta.
Questo ti dà anche l'opportunità di correggere anche la tua regex. Dovresti davvero usare un modello che corrisponda a uno o più spazi vuoti orizzontali finali, non solo a zero di essi, o correrai più lentamente dalla copia non necessaria. Cioè, questo:
s/[ \t]*$//
dovrebbe essere
s/[ \t]+$//
Tuttavia, come far capire a sed (1) che richiede un'estensione non POSIX, di solito sia -R
per System Ⅴ Unices come Solaris o Linux, sia -E
per quelli BSD come OpenBSD o MacOS. Ho il sospetto che sia impossibile sotto AIX. È purtroppo più facile scrivere una shell portatile che uno script di shell portatile, lo sai.
Avviso su 0xA0
Sebbene questi siano gli unici caratteri orizzontali dello spazio bianco in ASCII, sia ISO-8859-1 che, di conseguenza, anche Unicode hanno lo NO-BREAK SPACE al punto di codice U + 00A0. Questo è uno dei primi due caratteri non ASCII trovati in molti corpora Unicode, e ultimamente ho visto molte persone rompere il codice regex perché se ne sono dimenticate.
Quindi perché non lo fai e basta:
$ find * -print0 | perl -0 -nle 'print if -f && -T' | xargs -0 perl -i -pe 's/[\t\xA0 ]+$//'
Se si potrebbe avere i file UTF-8 da affrontare, aggiungere -CSD
, e se si esegue Perl v5.10 o superiore, è possibile utilizzare \h
per spazi orizzontali e \R
per un'interruzione di linea generica, che comprende \r
, \n
, \r\n
, \f
, \cK
, \x{2028}
, e \x{2029}
:
$ find * -print0 | perl -0 -nle 'print if -f && -T' | xargs -0 perl -CSD -i -pe 's/\h+(?=\R*$)//'
Funzionerà su tutti i file UTF-8 indipendentemente dalle interruzioni di riga, eliminando lo spazio bianco orizzontale finale (proprietà del carattere Unicode HorizSpace
) incluso il fastidioso NO-BREAK SPACE che si verifica prima di un'interruzione di riga Unicode (include le combinazioni CRLF) alla fine di ogni riga.
È anche molto più portatile della versione sed (1), perché esiste solo un'implementazione perl (1), ma molti di sed (1).
Il problema principale che vedo rimanere lì è con find (1), poiché su alcuni sistemi veramente recalcitranti (sai chi sei, AIX e Solaris), non capirà la -print0
direttiva supercritica . Se questa è la tua situazione, allora dovresti semplicemente usare il File::Find
modulo direttamente da Perl e non usare altre utility Unix. Ecco una versione pura del tuo codice Perl che non si basa su nient'altro:
#!/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;
Se stai eseguendo solo file di testo ASCII o ISO-8859-1, va bene, ma se stai eseguendo file ASCII o UTF-8, aggiungi -CSD
agli switch nella chiamata interna a Perl.
Se hai codifiche miste di tutte e tre le ASCII, ISO-8859-1 e UTF-8, temo che tu abbia un altro problema. :( Dovrai capire la codifica in base al file e non c'è mai un buon modo per indovinarlo.
Unicode Whitespace
Per la cronaca, Unicode ha 26 diversi caratteri di spazi bianchi. Puoi usare l' utilità unichars per annusarli. Solo i primi tre caratteri orizzontali bianchi sono quasi mai visti:
$ 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