web-dev-qa-db-ja.com

この `find`コマンドを実行するにはどうすればよいですか?ただし、非バイナリファイルに対してのみですか?

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

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

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

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

8
John Feminella

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 {} \;

シェルによっては、バックスラッシュの一部が必要な場合と不要な場合があります。

4
Bert F

これはコマンドラインで実行できます。

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

最も簡単で移植性の高い答えは、これを実行することです。

_#!/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)が_-T_ filetest演算子をサポートしておらず、サポートしている場合はエンコーディングを認識しないことです。これは絶対にUTFを検出する必要があります- 8、事実上の標準Unicodeエンコーディング。

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

_$ find . -type f | Perl -nle 'print if -T' | xargs sed -i 's/[ \t]*$//'
_

ただし、ファイル名の空白に問題があるため、nullで終了する必要があります。

_$ find . -type f -print0 | Perl -0 -nle 'print if -T' | xargs -0 sed -i 's/[ \t]*$//'
_

Perlはすでに_find2Perl_を理解しているので、findではなく_-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つには、POSIXバージョンのsed(1)は_-i_を理解しないのに対し、Perlのすべてのバージョンは理解するため、より移植性が高くなります。昨日のバージョンのsedは、tiが最初に表示されるPerlの非常に便利な_-i_オプションを愛情を込めて割り当てました。

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

_ s/[ \t]*$//
_

する必要があります

_ s/[ \t]+$//
_

ただし、sed(1)を取得して、POSIX以外の拡張機能が必要であることを理解する方法。通常、SolarisやLinuxなどのSystemⅤUnicesの場合は_-R_、または_-E_ OpenBSDやMacOSなどのBSDの場合。 AIXでは不可能だと思います。ポータブルシェルスクリプトよりもポータブルシェルを書く方が悲しいことに簡単です。

0xA0に関する警告

これらはASCIIの唯一の水平方向の空白文字ですが、ISO-8859-1とその結果としてUnicodeの両方で、コードポイントU + 00A0にノーブレークスペースがあります。これは、多くのUnicodeコーパスに見られる上位2つの非ASCII文字の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ファイルで機能し、Unicodeの改行(CRLFコンボを含む)の前に発生する厄介なNO-BREAK SPACEを含む末尾の水平方向の空白(Unicode文字プロパティHorizSpace)を取り除きます。各行の終わり。

また、Perl(1)の実装は1つしかないため、sed(1)バージョンよりもはるかに移植性が高くなります。しかし、sed(1)の多く。

私がそこに残っていると思う主な問題は、find(1)にあります。なぜなら、いくつかの本当に扱いにくいシステム(AIXとSolarisを知っている)では、超臨界_-print0_ディレクティブ。それがあなたの状況であるならば、あなたはPerlから直接_File::Find_モジュールを使うべきであり、他の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ファイルで実行している場合は、 Perlへの内部呼び出しのスイッチへの_-CSD_。

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
_
3
tchrist

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が問題である場合、UTF8サポートを使用してビルドされたgrepのビルドがあれば、tchristの回答と私の答えで十分です(ただし、通常、パッケージメンテナーはそのような機能を提供しようとします)。

0