この「find」コマンドを実行するにはどうすればよいのですか?


8

再帰的なディレクトリ階層内のすべてのファイルから末尾の空白を削除したい。私はこれを使います:

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

これは機能しますが、見つかったバイナリファイルから末尾の「空白」も削除されます。これは望ましくありません。

findバイナリファイルでこのコマンドを実行しないようにするにはどうすればよいですか?


Unixファイルシステムは、「バイナリ」ファイルと「非バイナリ」ファイルを区別しません。ファイル内を見ない限り、ファイル内のデータの種類を判別する方法はありません。
Wooble

@Wooble:それは正しいですがfile、データを検査できるようなコマンドがあります。
John Feminella、2011

回答:


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コマンドはこれらすべてに対して常に正しいことを行うとは限りません。また-isedのオプションがないシステムもあります。移植可能なシェルコマンドを書くのは難しいですよね?
-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;

以下で理由を説明します。ここでは、コマンドラインだけを使用してそれを行う方法と、ISO-8859-1(Latin-1)やUTF-8などのトランスASCIIテキストファイルを処理する方法も示します。 -それらのASCII空白。


物語の残り

問題は、find(1)が-Tfiletest演算子をサポートしておらず、サポートしている場合でもエンコーディングを認識しないことです。これは、事実上標準のUnicodeエンコーディングであるUTF-8を検出する必要があります。

あなたができることは、バイナリファイルを捨てるレイヤーを通してファイル名リストを実行することです。例えば

$ 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が、find2perlPerlは理解しているので、-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。そうしないと、デバイススペシャルを開いたり、FIFOをブロックしたりするリスクがあります。

ただし、それをすべて行う場合は、sed(1)を完全にスキップすることもできます。1 つにはsed(1)のPOSIXバージョンではが理解されないため-i、より移植性が高くなりますが、Perlのすべてのバージョンでは理解できます。Leddayバージョンのsedは-i、tiが最初に現れるPerlの非常に便利なオプションを愛情を込めて適用しました。

これにより、正規表現を修正することもできます。実際には、ゼロではなく、1つ以上の末尾の水平方向の空白に一致するパターンを使用する必要があります。そうしないと、不必要なコピーによって速度が低下します。つまり、これ:

 s/[ \t]*$//

する必要があります

 s/[ \t]+$//

ただし、sed(1)がPOSIX以外の拡張機能を必要とすることを理解するには、通常、-RSolarisやLinuxのようなSystemⅤUnices、または-EOpenBSDやMacOSのようなBSDの場合です。AIXでは不可能だと思います。残念ながら、移植可能なシェルスクリプトよりも移植可能なシェルを書く方が簡単です。

0xA0に関する警告

これらはASCIIでの唯一の水平方向の空白文字ですが、ISO-8859-1とその結果としてUnicodeの両方とも、コードポイントU + 00A0にNO-BREAK SPACEがあります。これは、多くのUnicodeコーパスで見つかった非ASCII文字の上位2つのうちの1つです。私は最近、多くの人々がそれを忘れたため、正規表現のコードが壊れるのを見てきました。

だから、なぜこれをしないのですか?

$ 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ファイルで機能しHorizSpace、各行の終わりにあるUnicode改行(CRLFコンボを含む)の前に発生する厄介なNO-BREAK SPACEを含む末尾の水平空白(Unicode文字プロパティ)を取り除きます。

また、perl(1)の実装は1 つだけですが、sed(1)の多くが存在するため、sed(1)バージョンよりもはるかに移植性があります。

そこに残っていると思う主な問題はfind(1)にあります。本当に手に負えないシステム(AIXとSolarisを知っている人もいます)では、超臨界-print0ディレクティブを理解できないからです。それがあなたの状況であれば、File::FindPerl のモジュールを直接使用し、他の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の3つすべてのエンコーディングが混在している場合、別の問題が発生することを恐れています。:(ファイルごとにエンコーディングを理解する必要がありますが、それを推測する良い方法は決してありません。

Unicode空白

記録として、Unicodeには26種類の空白文字があります。unicharsユーティリティを使用してこれらを探知できます。ほとんどの場合、最初の3つの水平方向の空白文字のみが表示されます。

$ 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(バイナリファイルを無視します)テキストファイルのみに一致し、PPerl互換の正規表現構文のためです。

perl部分は、ファイルをインプレースで変更し、末尾のスペース/タブをすべて削除します。

最後に:UTF8が問題である場合、grepあなたのビルドがUTF8サポートでビルドされていれば(通常、パッケージメンテナがその種の機能を提供しようとしますが)、tchristの答えと私のもので十分です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.