Самый простой и самый переносимый ответ - запустить это:
#!/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;
Ниже я объясню, почему, где я также показываю, как это сделать, используя только командную строку, а также как обращаться с текстовыми файлами trans-ASCII, такими как ISO-8859-1 (Latin-1) и UTF-8, которые после -Ассии пробелов в них.
Остальная часть истории
Проблема в том, что find (1) не поддерживает -T
оператор filetest и не распознает кодировки, если он это сделал - что вам абсолютно необходимо для обнаружения UTF-8, де-факто стандартной кодировки Unicode.
Что вы можете сделать, это запустить список имен файлов через слой, который выбрасывает двоичные файлы. Например
$ find . -type f | perl -nle 'print if -T' | xargs sed -i 's/[ \t]*$//'
Однако теперь у вас есть проблемы с пробелами в ваших именах файлов, поэтому вам нужно опоздать с нулевым завершением:
$ find . -type f -print0 | perl -0 -nle 'print if -T' | xargs -0 sed -i 's/[ \t]*$//'
Еще одна вещь, которую вы можете сделать, это не использовать, find
но find2perl
, поскольку Perl -T
уже понимает :
$ find2perl * -type T -exec sed 's/[ \t]*$//' -i {} \; | perl
И если вы хотите, чтобы Perl предполагал, что его файлы находятся в UTF-8, используйте
$ find2perl * -type T -exec sed 's/[ \t]*$//' -i {} \; | perl -CSD
Или вы можете сохранить полученный скрипт в файле и отредактировать его. Вы действительно должны не просто запустить -T
filetest для любого старого файла, а только для тех, которые являются простыми файлами, как определено вначале -f
. В противном случае вы рискуете открыть специальные устройства, заблокировать на пятерках и т. Д.
Однако, если вы собираетесь делать все это, вы можете вообще пропустить sed (1). Во-первых, он более переносим, поскольку POSIX-версия sed (1) не понимает -i
, а все версии Perl - понимают . Версии sed для последних дней с любовью переняли очень полезную -i
опцию в Perl, где она впервые появляется.
Это также дает вам возможность исправить ваши регулярные выражения. Вы действительно должны использовать шаблон, который соответствует одному или нескольким конечным горизонтальным пробелам, а не просто их нулю, или вы будете работать медленнее из-за ненужного копирования. То есть это:
s/[ \t]*$//
должно быть
s/[ \t]+$//
Однако, как получить sed (1), чтобы понять, что для этого требуется расширение, -R
отличное от POSIX, как правило, для System System Unices, таких как Solaris или Linux, или -E
для BSD, таких как OpenBSD или MacOS. Я подозреваю, что это невозможно под AIX. Знаете, проще написать переносную оболочку, чем переносимый сценарий оболочки.
Предупреждение о 0xA0
Хотя это единственные горизонтальные пробельные символы в ASCII, оба стандарта ISO-8859-1 и, следовательно, также Unicode имеют пробел NO-BREAK в кодовой точке U + 00A0. Это один из двух лучших не-ASCII символов, встречающихся во многих Unicode-корпусах, и в последнее время я видел, как многие люди ломали код регулярного выражения, потому что они забыли об этом.
Так почему бы тебе просто не сделать это:
$ find * -print0 | perl -0 -nle 'print if -f && -T' | xargs -0 perl -i -pe 's/[\t\xA0 ]+$//'
Если у вас может быть UTF-8 файлов для решения, дополнения -CSD
, и если вы работаете на Perl v5.10 или выше, вы можете использовать \h
для горизонтального пробельных и \R
для общего LineBreak, который включает в себя \r
, \n
, \r\n
, \f
, \cK
, \x{2028}
, и \x{2029}
:
$ find * -print0 | perl -0 -nle 'print if -f && -T' | xargs -0 perl -CSD -i -pe 's/\h+(?=\R*$)//'
Это будет работать со всеми файлами UTF-8, независимо от их разрывов строк, избавляя от конечного горизонтального пробела (свойство символа Unicode HorizSpace
), включая надоедливый пробел NO-BREAK, который возникает перед разрывом строки Unicode (включая комбинации CRLF) в конце каждой строки.
Он также гораздо более переносим, чем версия sed (1), потому что существует только одна реализация perl (1), но много sed (1).
Основная проблема, которую я вижу, остается с find (1), поскольку в некоторых действительно непокорных системах (вы знаете, кто вы, AIX и Solaris) она не понимает -print0
директиву со сверхкритическими параметрами . Если это ваша ситуация, то вы должны просто использовать File::Find
модуль из Perl напрямую и не использовать никаких других утилит Unix. Вот чистая Perl-версия вашего кода, которая не полагается ни на что другое:
#!/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;
Если вы работаете только с текстовыми файлами ASCII или ISO-8859-1, это нормально, но если вы работаете с файлами ASCII или UTF-8, добавьте -CSD
переключатели во внутреннем вызове Perl.
Если у вас смешанные кодировки всех трех ASCII, ISO-8859-1 и UTF-8, то, боюсь, у вас есть другая проблема. :( Вам придется выяснить кодировку для каждого файла, и никогда не бывает хорошего способа угадать это.
Unicode Пробелы
Для записи, Unicode имеет 26 различных пробельных символов. Вы можете использовать в unichars утилиту для нюхать эти вне. Только первые три горизонтальных пробела встречаются почти всегда:
$ 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