A resposta mais simples e portátil é executar isso:
#!/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;
Eu explico por que abaixo, onde eu também mostro como fazer isso usando apenas a linha de comando, assim como como lidar com arquivos de texto trans-ASCII como ISO-8859-1 (Latin-1) e UTF-8, que aften tem espaços em branco não-ASCII neles.
O restante da história
O problema é que find (1) não suporta o operador -T
filetest, nem reconhece codificações se o fizer - o que é absolutamente necessário para detectar UTF-8, de fato codificação Unicode padrão.
O que você poderia fazer é executar a lista de nomes de arquivos por meio de uma camada que lança arquivos binários. Por exemplo
$ find . -type f | perl -nle 'print if -T' | xargs sed -i 's/[ \t]*$//'
No entanto, agora você tem problemas com espaços em branco nos seus nomes de arquivos, portanto, é necessário atrasar isso com a terminação nula:
$ find . -type f -print0 | perl -0 -nle 'print if -T' | xargs -0 sed -i 's/[ \t]*$//'
Outra coisa que você pode fazer é usar não find
mas find2perl
, pois Perl entende -T
já:
$ find2perl * -type T -exec sed 's/[ \t]*$//' -i {} \; | perl
E se você quiser que o Perl assuma que seus arquivos estão em UTF-8, use
$ find2perl * -type T -exec sed 's/[ \t]*$//' -i {} \; | perl -CSD
Ou você pode salvar o script resultante em um arquivo e editá-lo. Você realmente não deve executar apenas o -T
filetest em qualquer arquivo antigo, mas apenas naqueles que são arquivos simples, conforme determinado pela primeira vez por -f
. Caso contrário, você corre o risco de abrir dispositivos especiais, bloquear em fifos, etc.
No entanto, se você for fazer tudo isso, você pode pular sed (1) completamente. Por um lado, é mais portátil, já que a versão POSIX de sed (1) não entende -i
, enquanto todas as versões do Perl fazem. As versões dos últimos dias de sed carinhosamente se apropriaram da muito útil -i
da Perl onde ti aparece pela primeira vez.
Isso também lhe dá a oportunidade de corrigir seu regex também. Você deve estar realmente usando um padrão que corresponda a um ou mais espaços em branco horizontais à direita, não apenas zero deles, ou você correrá mais lentamente a partir de cópias desnecessárias. Ou seja, isso:
s/[ \t]*$//
deve ser
s/[ \t]+$//
No entanto, como conseguir sed (1) entender que requer uma extensão não-POSIX, geralmente -R
para o System Ⅴ Unices como Solaris ou Linux, ou -E
para o BSD como o OpenBSD ou o MacOS. Eu suspeito que é impossível em AIX. É muito mais fácil escrever um shell portátil do que um shell script portátil, você sabe.
Aviso em 0xA0
Embora esses sejam os únicos caracteres de espaço em branco horizontal em ASCII, tanto o ISO-8859-1 quanto o Unicode possuem o NO-BREAK SPACE no ponto de código U + 00A0. Esse é um dos dois principais caracteres não-ASCII encontrados em muitos corpora Unicode e, recentemente, vi muitos códigos de expressão regular de pessoas quebrarem porque eles se esqueceram disso.
Então, por que você não faz isso:
$ find * -print0 | perl -0 -nle 'print if -f && -T' | xargs -0 perl -i -pe 's/[\t\xA0 ]+$//'
Se você tiver arquivos UTF-8, adicione -CSD
e, se estiver executando o Perl v5.10 ou posterior, use \h
para espaço em branco horizontal e \R
para um quebra de linha genérica, que inclui \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*$)//'
Isso funcionará em todos os arquivos UTF-8, independentemente de suas quebras de linha, livrando-se do espaço em branco horizontal (propriedade de caractere Unicode HorizSpace
) incluindo o incômodo NO-BREAK SPACE que ocorre antes de um quebra de linha Unicode (incluir combos CRLF) em o final de cada linha.
Também é muito mais portátil do que a versão sed (1), porque existe apenas uma implementação perl (1), mas muitos sed (1).
O principal problema que vejo é o find (1), já que em alguns sistemas verdadeiramente recalcitrantes (você sabe quem você é, AIX e Solaris), ele não entende o% supercrítico diretiva-print0
. Se esta é a sua situação, então você deve apenas usar o módulo File::Find
do Perl diretamente, e não usar outros utilitários Unix. Aqui está uma versão em Perl pura do seu código que não depende de mais nada:
#!/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 você estiver executando apenas arquivos de texto ASCII ou ISO-8859-1, tudo bem, mas se você estiver executando com arquivos ASCII ou UTF-8, adicione -CSD
aos switches na chamada interna para Perl.
Se você tiver codificações mistas de todos os três ASCII, ISO-8859-1 e UTF-8, receio que você tenha outro problema. :( Você terá que descobrir a codificação em uma base por arquivo, e nunca há uma boa maneira de adivinhar isso.
Espaços em branco Unicode
Para o registro, o Unicode tem 26 caracteres diferentes em branco. Você pode usar o o utilitário unichars para identificá-los. Somente os primeiros três caracteres horizontais de espaço em branco quase são vistos:
$ 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