シェルスクリプトでバッファにデータを追加するには?

シェルスクリプトでバッファにデータを追加するには?

シェルスクリプトを使って次のことをしたいと思います。 (単純化のためにINPUTに同じデータを使用しています。実際の生活では、ループラベルjjに応じてデータが変更されます。)

#!/bin/sh
for jj in `seq 100`; do
    cat INPUT.file >> OUTPUT.file
done

しかし、これはファイルを開閉する操作がループで行われるため、非常に非効率的です。 INPUT.fileが大きいと、このコードが非常に遅くなる可能性があります。それで、Cで事前に割り当てられた変数を作成するのと同じように、バッファを持つか生成する方法があるかどうか疑問に思います。

答え1

ありがとうスティーブン・チャジェラス回答「echoとcatの実行時になぜこんなに大きな違いがあるのですか?」、正解はムル一度だけ呼び出すとcat改善することができます(ただし、データが大きくループの繰り返しが多い場合は、この「わずかな」量が増える可能性があります。私のシステムでは、このスクリプトはループスクリプトにかかる時間の約75%を占めます)。

#!/bin/sh
yes INPUT.file | head -100 | xargs cat >> OUTPUT.file

答え2

リダイレクトループ自体を考えてみましょう。

#!/bin/sh
for jj in seq 100; do
    cat INPUT.file
done >> OUTPUT.file

答え3

速度が主な関心事である場合は、catこの作業を十分に早く完了できないことがわかります。コンポーネントファイルを出力に並列に書き込むことができます。

cat私は次の注意を払って並列高速バージョンを作成しました。

  1. すべての入力ファイルは通常のファイルでなければなりません(サイズを事前に知ることができます)。
  2. fcat実行中に入力ファイルを書き込んだり切ったりしないでください。
  3. 出力ファイルは既に存在することはできません(驚きを防ぎ、すぐに対処する内容を読むのに時間を無駄にしないため)。

明らかに、これは迅速な概念証明であるため、より強力にすることができますが、アイデアは次のとおりです。

fcat.c:

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>

#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>


struct in_fd {
    int fd;
    int err;
    off_t start;
    struct stat s;
};

int main(int argc, char**argv)
{
    char *outfile = argv[--argc];

    if (argc < 2) {
        fprintf(stderr, "Usage: %s INFILE... OUTFILE\n", argv[0]);
        return 1;
    }

    struct in_fd *infiles = calloc(argc, sizeof *infiles);

#pragma omp parallel for
    for (int i = 1;  i < argc;  ++i) {
        struct in_fd *const input = infiles + i;
        char const *const filename = argv[i];
        input->err = 0;
        if ((input->fd = open(filename, O_RDONLY)) < 0) {
            perror(filename);
            input->err = errno;
            continue;
        }
        if (fstat(input->fd, &input->s)) {
            perror(filename);
            input->err = errno;
            continue;
        }
        if (!S_ISREG(input->s.st_mode)) {
            fprintf(stderr, "%s: not a regular file\n", filename);
            input->err = EINVAL;
            continue;
        }
    }

    off_t total = 0;
    for (int i = 1;  i < argc;  ++i) {
        if (infiles[i].err)
            return EXIT_FAILURE;
        infiles[i].start = total;
        total += infiles[i].s.st_size;
    }

    int out_fd = open(outfile, O_RDWR | O_CREAT | O_EXCL, 0666);
    if (out_fd < 1) {
        perror(outfile);
        return 1;
    }

    if (ftruncate(out_fd, total)) {
        perror(outfile);
        return 1;
    }

    /* On Linux, you might wish to add MAP_HUGETLB */
    char *out_mem = mmap(NULL, total, PROT_WRITE, MAP_SHARED, out_fd, 0);
    if (out_mem == MAP_FAILED) {
        perror(outfile);
        return 1;
    }

#pragma omp parallel for
    for (int i = 1;  i < argc;  ++i) {
        struct in_fd *const input = infiles + i;
        char *p = out_mem + input->start;
        char *end = p + input->s.st_size;
        input->err = 0;
        while (p < end) {
            int r = read(input->fd, p, end-p);
            if (r < 0) {
                if (errno != EINTR) {
                    perror(argv[i]);
                    input->err = errno;
                    break;
                }
            } else {
                p += r;
            }
        }
        close(infiles->fd);
    }


    if (munmap(out_mem, total)) {
        perror(outfile);
    }

    for (int i = 1;  i < argc;  ++i) {
        if (infiles[i].err) {
            unlink(outfile);
            return EXIT_FAILURE;
        }
    }

    return EXIT_SUCCESS;
}

ファイル生成:

CFLAGS += -Wall -Wextra
CFLAGS += -std=c99 -D_GNU_SOURCE
CFLAGS += -g -O2
CFLAGS += -fopenmp

all: fcat
.PHONY:all

私の12スレッドの私のタイミングは、0.2秒のランタイムと2.3秒のランタイムを示しましたcat(それぞれ3つの実行、ホットキャッシュの使用、48のファイル、合計138M)。

関連情報