Як запустити цю команду `find`, але лише для небінарних файлів?


8

Я хочу видалити пробіл пробілу з усіх файлів рекурсивної ієрархії каталогів. Я використовую це:

find * -type f -exec sed 's/[ \t]*$//' -i {} \;

Це працює, але також видалить пробіли "пробілів" з знайдених бінарних файлів, що небажано.

Як мені сказати, findщоб уникнути запуску цієї команди у бінарних файлах?


Файлові системи Unix не розрізняють "бінарні" та "небінарні" файли; немає способу визначити, який тип даних є у файлі, не заглядаючи всередину нього.
Wooble

@Wooble: Це правильно, але є такі команди, fileякі можуть перевірити дані.
Джон Фемінелла

Відповіді:


4

Ви можете спробувати скористатися командою Unix, fileщоб допомогти визначити файли, які ви не хочете, але я думаю, що може бути краще, якщо ви чітко вкажете, які файли ви хочете звернути, а не ті, які ви не маєте.

find * -type f \( -name \*.java -o -name \*.c -o -name \*.sql \) -exec sed 's/[ \t]*$//' -i {} \;

щоб уникнути проникнення у файли керування джерелами, можливо, ви хочете щось подібне

find * \! \( -name .svn -prune \) -type f \( -name \*.java -o -name \*.c -o -name \*.sql \) -exec sed 's/[ \t]*$//' -i {} \;

Можливо, вам можуть знадобитися деякі накипи в залежності від вашої оболонки.


2
Я не знаю про вас, але всі наші вихідні файли Java завжди знаходяться в стандартному UTF-8, так що команда sed не завжди буде робити все правильно. У мене також є системи без -iможливості sed . Важко написати портативну команду оболонки, чи не так?
tchrist

4

Це можна зробити в командному рядку.

$ find . -type f -print|xargs file|grep ASCII|cut -d: -f1|xargs sed 's/[ \t]*$//' -i

3

Найпростіший і портативний варіант відповіді:

#!/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

0

GNU grep досить добре визначає, чи файл є двійковим чи ні. Крім Solaris, я впевнений, що є інші платформи, які за замовчуванням не встановлені GNU grep, але, як і Solaris, я впевнений, що ви можете встановити його.

perl -pi -e 's{[ \t]+$}{}g' `grep -lRIP '[ \t]+$' .`

Якщо ви знаходитесь у Solaris, ви замінили б grepна /opt/csw/bin/ggrep.

Ці grepпрапори виконують такі дії : lтільки списки імен файлів для зіставлення файлів, Rє рекурсивним, Iвідповідає тільки текстові файли (ігнорує виконавчі файли), таP також для Perl-сумісний синтаксис регулярних виразів.

Частина perl модифікує файл на місці, видаляючи всі пробіли / вкладки.

Нарешті: якщо UTF8 є проблемою, відповідь tchrist у поєднанні з моєю має бути достатньою, за умови, що складене grepвами було побудовано за допомогою підтримки UTF8 (хоча зазвичай обслуговуючі пакети намагаються надати такий функціонал).

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.