Самый простой и самый переносимый ответ - запустить это:
#!/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]*$//'
Другая вещь, которую вы можете сделать, это использовать not 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). Во-первых, он более переносим, поскольку версия sed(1) для POSIX не понимает -i
, в то время как все версии Perl понимают. Версии sed последних дней с любовью присваивают очень полезную опцию -i
из Perl, где впервые появляется ti.
Это также дает вам возможность исправить ваши регулярные выражения. Вы действительно должны использовать шаблон, который соответствует одному или нескольким конечным горизонтальным пробелам, а не просто их нулю, иначе вы будете медленнее работать из-за ненужного копирования. То есть это:
s/[ \t]*$//
должно быть
s/[ \t]+$//
Однако, как получить sed(1), чтобы понять, что для этого требуется расширение, отличное от POSIX, обычно это либо -R
для системных 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
для общего переноса строки, который включает в себя \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) она не понимает директиву supercritical -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