web-dev-qa-db-ja.com

すべての「非バイナリ」ファイルを検索する

findコマンドを使用して、ディレクトリ内のすべての「非バイナリ」ファイルを見つけることは可能ですか?これが私が解決しようとしている問題です。

Windowsユーザーからファイルのアーカイブを受け取りました。このアーカイブには、ソースコードと画像ファイルが含まれています。私たちのビルドシステムは、Windowsの行末を持つファイルでは、Niceを再生しません。コマンドラインプログラム(flip -u)* nixとウィンドウの間で行末を反転します。だから、私はこのようなことをしたいと思います

find . -type f | xargs flip -u

ただし、このコマンドをイメージファイルまたは他のバイナリメディアファイルに対して実行すると、ファイルが破損します。ファイル拡張子のリストを作成し、それを使ってフィルタリングできることはわかっていますが、そのリストを最新の状態に保つことに依存していないものが欲しいです。

それで、ディレクトリツリーですべての非バイナリファイルを見つける方法はありますか?または、私が考慮すべき代替ソリューションはありますか?

46
Alan Storm

私はfileを使用して出力をgrepまたはawkにパイプし、テキストファイルを見つけてから、fileの出力のファイル名部分のみを抽出してxargsにパイプします。

何かのようなもの:

file * | awk -F: '/ASCII text/ {print $1}' | xargs -d'\n' -r flip -u

Grepは単なる「テキスト」ではなく「ASCIIテキスト」を検索することに注意してください。リッチテキストドキュメントやUnicodeテキストファイルなどをいじりたくない場合があります。

find(または何でも)を使用して、fileで調べるファイルのリストを生成することもできます。

find /path/to/files -type f -exec file {} + | \
  awk -F: '/ASCII text/ {print $1}' | xargs -d'\n' -r flip -u

Xargsの-d'\n'引数により、xargsは各入力行を個別の引数として扱い、スペースやその他の問題のある文字を含むファイル名に対応します。つまり、入力ソースがNULLで区切られた出力を生成しない、または生成できない場合、xargs -0の代わりになります(find-print0オプションなど)。変更ログによると、xargsは2005年9月に-d/--delimiterオプションを取得したので、非古代のLinuxディストリビューションに含める必要があります(確認しなかったので、確認しました-漠然と覚えていました「最近」の追加でした)。

改行はファイル名の有効な文字であるため、ファイル名に改行が含まれていると改行されます。典型的なUNIXユーザーの場合、これは病理学的に異常なことですが、ファイルがMacまたはWindowsマシンで作成されたものであるかどうかは前兆ではありません。

また、fileは完全ではないことに注意してください。ファイル内のデータの種類を検出するのは非常に優れていますが、混乱することがあります。

この方法の多くのバリエーションを過去に何度も使用して成功しました。

21
cas

いいえ。バイナリファイルまたは非バイナリファイルについて特別なことは何もありません。 「0x01–0x7Fの文字のみを含む」のようなヒューリスティックを使用できますが、これは非ASCII文字のバイナリファイルを含むテキストファイル、および不運なバイナリファイルのテキストファイルを呼び出します。

さて、それを無視したら...

Zipファイル

WindowsユーザーからZipファイルとして送信された場合、Zip形式では、アーカイブ自体でファイルをバイナリまたはテキストとしてマークできます。 unzipの-aこれに注意を払い、変換するオプション。もちろん、これが良い考えではない理由については、最初のパラグラフを参照してください(Zipプログラムは、アーカイブを作成したときに間違ったと推測した可能性があります)。

zipinfoは、zipfileリストでバイナリ(b)またはテキスト(t)のファイルを通知します。

他のファイル

Fileコマンドはファイルを見て、それを識別しようとします。特に、おそらく-i(出力MIMEタイプ)オプションは便利です。タイプがtext/*のファイルのみを変換します

9
derobert

受け入れられた答えは私のためにそれらのすべてを見つけられませんでした。以下は、grepの-Iを使用してバイナリを無視し、すべての隠しファイルを無視する例です...

find . -type f -not -path '*/\.*' -exec grep -Il '.' {} \; | xargs -L 1 echo 

ここでは実際のアプリケーションで使用されています:dos2unix

https://unix.stackexchange.com/a/365679/11219

8
phyatt

file -b --mime-encodingを使用してbashの非バイナリファイルのみを処理するための一般的なソリューション:

while IFS= read -d '' -r file; do
  [[ "$(file -b --mime-encoding "$file")" = binary ]] &&
    { echo "Skipping   $file."; continue; }

  echo "Processing $file."

  # ...

done < <(find . -type f -print0)

私はfileユーティリティの作者に連絡し、彼はバージョン5.26(2016-04-16でリリースされた、たとえば現在のArchおよびUbuntu 16.10にある)に-00パラメータを追加してfile\0result\0は、一度に複数のファイルが供給された場合に、次のように実行できます。例:

find . -type f -exec file -00 --mime-encoding {} + |
  awk 'BEGIN{ORS=RS="\0"}{if(NR%2)f=$0;else if(!/binary/)print f}' | …

awkの部分は、バイナリではないすべてのファイルを除外するためのものです。ORSは出力セパレーターです。)

もちろんループでも使用できます:

while IFS= read -d '' -r file; do

  echo "Processing $file."

  # ...

done < <(find . -type f -exec file -00 --mime-encoding {} + |
  awk 'BEGIN{ORS=RS="\0"}{if(NR%2)f=$0;else if(!/binary/)print f}')

これと以前のものに基づいて、バイナリファイルをフィルタリングするための小さなbashスクリプトを作成しました。これは、新しいバージョンのfile-00パラメータを使用して新しい方法を利用し、フォールバックします。古いバージョンの以前の方法に:

#!/bin/bash

# Expects files as arguments and returns the ones that do
# not appear to be binary files as a zero-separated list.
#
# USAGE:
#   filter_binary_files.sh [FILES...]
#
# EXAMPLE:
#   find . -type f -mtime +5 -exec ./filter_binary_files.sh {} + | xargs -0 ...
# 

[[ $# -eq 0 ]] && exit

if [[ "$(file -v)" =~ file-([1-9][0-9]|[6-9]|5\.([3-9][0-9]|2[6-9])) ]]; then
  file -00 --mime-encoding -- "$@" |
    awk 'BEGIN{ORS=RS="\0"}{if(NR%2)f=$0;else if(!/binary/)print f}'
else
  for f do
    [[ "$(file -b --mime-encoding -- "$f")" != binary ]] &&
      printf '%s\0' "$f"
  done
fi

または、ここではよりPOSIXなものですが、sort -Vのサポートが必要です。

#!/bin/sh

# Expects files as arguments and returns the ones that do
# not appear to be binary files as a zero-separated list.
#
# USAGE:
#   filter_binary_files.sh [FILES...]
#
# EXAMPLE:
#   find . -type f -mtime +5 -exec ./filter_binary_files.sh {} + | xargs -0 ...
# 

[ $# -eq 0 ] && exit

if [ "$(printf '%s\n' 'file-5.26' "$(file -v | head -1)" | sort -V)" = \
    'file-5.26' ]; then
  file -00 --mime-encoding -- "$@" |
    awk 'BEGIN{ORS=RS="\0"}{if(NR%2)f=$0;else if(!/binary/)print f}'
else
  for f do
    [ "$(file -b --mime-encoding -- "$f")" != binary ] &&
      printf '%s\0' "$f"
  done
fi
7
phk
find . -type f -exec grep -I -q . {} \; -print

これにより、すべての通常のファイル(-type fgrepが空ではなくバイナリではないと考える現在のディレクトリ(またはそれ以下)内。

それは使用しています grep -Iバイナリファイルと非バイナリファイルを区別します。 -Iフラグ。ファイルがバイナリであることを検出すると、grepをゼロ以外の終了ステータスで終了します。 「バイナリ」ファイルは、grepによると、印刷可能なASCII範囲外の文字を含むファイルです。

-qオプションをgrepに指定すると、指定されたパターンが見つかった場合、データを出力せずに、終了ステータス0で終了します。使用するパターンは単一のドットで、これは任意の文字に一致します。

ファイルが非バイナリであることが判明し、少なくとも1文字が含まれている場合は、ファイルの名前が出力されます。

勇気を感じたら、flip -uもそれに入れます:

find . -type f -exec grep -I -q . {} \; -print -exec flip -u {} \;
4
Kusalananda

Casの答え は良いですが、saneファイル名を想定しています。特に、ファイル名には改行が含まれないことが想定されています。

このケースを正しく処理することは非常に単純(そして私の考えでは実際にはよりクリーン)であるため、ここでこの仮定を行う正当な理由はありません。

find . -type f -exec sh -c 'file "$1" | grep -q "ASCII text"' sh {} \; -exec flip -u {} \;

findコマンドは POSIXで指定された機能 のみを使用します。ブールテストとして-execを使用して任意のコマンドを実行すると、シンプルで堅牢(奇数ファイル名を正しく処理)で、-print0よりも移植性が高くなります。

実際、flipを除いて、コマンドのすべての部分はPOSIXで指定されています。

fileは、返される結果の正確性を保証するものではないことに注意してください。ただし、実際には、出力内の「ASCIIテキスト」のgreppingは非常に信頼できます。

(それはmiss一部のテキストファイルかもしれませんが、バイナリファイルを「ASCIIテキスト」として誤って識別し、それを壊すことはほとんどありません。警告の側に誤りがあります。)

4
Wildcard

これを試して :

find . -type f -print0 | xargs -0 -r grep -Z -L -U '[^         -~]' | xargs -0 -r flip -u

grep '[^ -~]'の引数は'[^<tab><space>-~]'です。

シェルコマンドラインで入力する場合は、次のように入力します。 Ctrl+V 前 Tab。エディタでは問題ないはずです。

  • '[^<tab><space>-~]'は、ASCIIテキストではない任意の文字に一致します(改行はgrepによって無視されます)。
  • -Lは、一致しないファイルのファイル名のみを出力します
  • -Zは、ヌル文字で区切られたファイル名を出力します(xargs -0の場合)
1
Vouze

代替ソリューション:

Dos2unixコマンドは、行末をWindows CRLFからUnix LFに変換し、バイナリファイルを自動的にスキップします。私はそれを再帰的に適用します:

find . -type f -exec dos2unix {} \;
1
Spark

Sudo find /(-type f -and -path '* /git/ *' -iname 'README')-exec grep -liI '100644\| 100755 '{} \; -exec flip -u {} \;

i。(-type f -and -path '* /git/ *' -iname 'README'):を含むパス内のファイルを検索しますgitとREADMEという名前のファイルに名前を付けます。特定のフォルダとファイル名がわかっている場合は、それを検索すると便利です。

ii.-execコマンドは、findによって生成されたファイル名に対してコマンドを実行します

iii。\;コマンドの終わりを示します

iv。{}は、前回の検索で見つかったファイル/フォルダー名の出力です。

v。複数のコマンドを後で実行できます。 -exec "command"を追加することにより、\; -exec flip -u \;など

vii.grep

1.-l lists the name of the file
2.-I searches only non-binary files
3.-q quiet output
4.'100644\|100755' searches for either 100644 or 100755 within the file found. if found it then runs flip -u. \| is the or operator for grep. 

このテストディレクトリのクローンを作成して試すことができます: https://github.com/alphaCTzo7G/stackexchange/tree/master/linux/findSolution204092017

ここでより詳細な回答: https://github.com/alphaCTzo7G/stackexchange/blob/master/linux/findSolution204092017/README.md

0
alpha_989