Найпростіший і портативний варіант відповіді:
#!/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, у яких aften не мають -ASCII пробіли в них.
Решта історії
Проблема в тому, що знайти (1) не підтримує -T
оператора файлових тестів, а також не розпізнає кодування, якщо це було - що вам абсолютно потрібно для виявлення 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
тест файлів на будь-якому старому файлі, а лише на тих, які є звичайними файлами, як спочатку визначається-f
. Інакше ви ризикуєте відкрити спеціальні пристрої, заблокувати фіфоси тощо.
Однак якщо ви збираєтеся все це зробити, ви можете повністю пропустити sed (1). З одного боку, він більш портативний, оскільки POSIX версія sed (1) не розуміє -i
, тоді як усі версії Perl так і є. Останні версії sed з любов'ю привласнили дуже корисний -i
варіант з Perl, де ти вперше з'являється.
Це також дає можливість виправити ваш регулярний вираз. Ви дійсно повинні використовувати шаблон, який відповідає одному або декільком заднім горизонтальним пробілом, а не лише їх нулю, або ви будете бігати повільніше від непотрібного копіювання. Тобто це:
s/[ \t]*$//
має бути
s/[ \t]+$//
Однак, як змусити sed (1) зрозуміти, що потрібно не-POSIX-розширення, як правило, -R
для System Ⅴ Unice, наприклад Solaris або Linux, або-E
для BSD, таких як OpenBSD або MacOS. Я підозрюю, що це неможливо під AIX. На жаль, простіше написати портативний оболонку, ніж сценарій портативної оболонки.
Попередження на 0xA0
Хоча це єдині символи горизонтального пробілу в ASCII, і ISO-8859-1, і, отже, Unicode мають NO-BREAK SPACE у кодовій точці 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).
Основна проблема, яку я бачу, є у пошуку (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