쉘 스크립트에서 버퍼에 데이터를 추가하는 방법은 무엇입니까?

쉘 스크립트에서 버퍼에 데이터를 추가하는 방법은 무엇입니까?

쉘 스크립트를 사용하여 다음을 수행하고 싶습니다. (단순화를 위해 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).

관련 정보