web-dev-qa-db-ja.com

Awk-作成されたファイル名をstdoutに指定しながら、各バイトを独自のファイルに分割します

巨大なファイル(数ギガバイトなど)があり、さまざまなツール(スプリットなど)を試しましたが、自分のニーズのためにawkでこれを行う必要があります。

基本的に、私は何をエミュレートしたいsplit -b 1 fileはawkで行います(提供されているマニュアルページとコマンドから、ファイルはそれぞれ1バイトずつ分割されます)。

また、スクリプトの実行中に、生成されたファイルのファイル名(インクリメント)がstdoutに出力されるようにしたいので、他のスクリプトで変数などとして使用できます。

編集:ここで私がこれまでに行ったこと

awk '{for(i=1;i<=length;i++) print substr($0, i, 1)}' filename

ファイル名の詳細

他のファイルを上書きしないように、ファイル名を増やす必要があります。 (数値または英数字)。

比較するために、coreutilsのsplitツールは、次のように文字を使用してファイル名を生成します:xa xb...xaa

私はそれを持っているか、可能であれば数値のみを使いたい:1 2..444または/および英数字のファイル名:a1 a2

2
Nordine Lotfi

GNU awkを使用すると、次のことができます。

LC_ALL=C gawk -v RS='.{1}' '
  {
    file = "filename" ++n
    print file
    printf "%s", RT > file
    close(file)
  }' < input

しかし、ほとんどのファイルシステムでは、バイトごとに1つのファイルを作成するため、ディスクスペース(ほとんどのファイルシステムでは1バイトのファイルが数キロバイトのディスクスペースを占有しているため)またはiノードがすぐに不足するか、パフォーマンスが恐ろしくなります。数十万バイトの入力後(ディレクトリにエントリを追加するコストは、いくつかのファイルシステムの実装があるディレクトリのサイズとともに増加するため)

  • LC_ALL=Cは、.が文字ではなくバイトで一致するためのものです
  • RS='.{1}'は、レコード区切り文字を1単一文字(LC_ALL=Cを含む1バイト)に設定します。 RS=.は、レコードセパレータがドット文字であることを意味するため機能しません。 RSを正規表現と見なすには、gawkを複数の文字にする必要があります。 (.).|.も機能しますが、私のテストでは、.{1}が3の中で最も効率的であることがわかりました。
  • RTには、RSに一致したテキストが含まれます。

正規表現としてのRS、バイナリデータを処理する機能、およびRTはすべて非標準の拡張機能です。 RTはGNU固有のAFAIKです。

4

splitが行うこと(1バイトのファイルを生成する場合)は、ファイルに順番に番号を付けることですが、最大256個の異なるファイルのみを生成します。ファイルの内容はありません。256だけです。

また、マルチギガバイトのファイルをそれぞれ1バイトの同量のファイルに変換すると、処理するデータのサイズが大きくなり(ext4ファイルシステムでは4000を超える)、各ファイルへのアクセスが遅くなります。

ただし、データに対して追加の処理を実行するとも言われている代替手段があります。

また、スクリプトの実行中に、生成されたファイルのファイル名(インクリメント)をstdoutに出力させたいので、他のスクリプトで変数などとして使用できます。

したがって、リソースの消費量(ディスク容量、処理能力、時間、エネルギー)を大幅に削減するはるかに高速なソリューションは次のとおりです。

  • 0x00から0xffまでの1バイトの256ファイルを生成します。これは可能な入力をカバーします。
  • Stdout a number + a file nameで生成します。番号は、最初からの入力ファイル内の位置です。ファイル名は、入力内のバイトの値を示すために上記で作成した256ファイルの1つです。

事前に256ファイルを生成できます(bash)。

for((i=0;i<=255;i++)); do 
    file=prefix$(printf '%03d' "$i"); 
    printf '%b' "$(printf '\\x%x' "$i")" >$file;
done

または、数ギガバイトのファイルの処理中に必要なものを生成します。

LC_ALL=C gawk '
  BEGIN{                                # 
      RS=".{1}"                         # set the record separator
      for(i=0;i<256;i++){
          ord[ sprintf("%c",i) ] = i    # help array ord
      }
  }
  {
    position = ++n                      # keep count of bytes read
    file = "prefix" ord[RT]             # find the file name to use
    if ( ! seen[file] ) {               # Have we seen this file ?
        printf "%s", RT > file          # If not, create it.
        close(file)                     # close the file
        seen[file]=1                    # record that we have seen it.
    }
    print position, file                # print information for next script
  }
' ./input                               # file to process.

つまり、より高速なソリューションです。

1
Isaac