Como anexar dados ao buffer no shell script?

4

Eu gostaria de seguir usando o shell script: (para simplificar, eu uso os mesmos dados para INPUT. No caso real, os dados mudam com o rótulo de loop jj)

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

No entanto, isso é muito ineficiente, já que abrir e fechar o arquivo está no loop. Quando o tamanho do INPUT.file for grande, esse código será muito lento. Então, eu estou querendo saber se existe uma maneira de ter / criar um buffer como criar uma variável pré-alocada em C.

    
por Xiaopeng Huang 16.09.2015 / 16:30

3 respostas

6

Graças a a resposta de Stéphane Chazelas a "Por que existe tal diferença? em tempo de execução de eco e gato? ", a resposta de muru pode ser um pouco melhorada por chamando cat apenas uma vez (no entanto, essa quantia "um pouco" pode se tornar muito para big data e numerosas iterações de loop; no meu sistema, esse script leva ~ 75% do tempo que o script leva):

#!/bin/sh
yes INPUT.file | head -100 | xargs cat >> OUTPUT.file
    
por 17.09.2015 / 11:46
4

Considere redirecionar o próprio loop:

#!/bin/sh
for jj in seq 100; do
    cat INPUT.file
done >> OUTPUT.file
    
por 16.09.2015 / 16:32
1

Se a velocidade é sua principal preocupação, você pode achar que cat não é rápido o suficiente nessa tarefa. Você pode escrever os arquivos constituintes para a saída em paralelo.

Eu criei uma versão rápida de um paralelo cat com as seguintes advertências:

  1. todos os arquivos de entrada devem ser arquivos regulares (por isso, sabemos o tamanho antecipadamente).
  2. não escreva ou trunque os arquivos de entrada enquanto fcat estiver em execução
  3. o arquivo de saída ainda não existe (para evitar acidentes e também para evitar perder tempo lendo o que estamos prestes a sobrescrever).

Obviamente, esta é uma prova rápida de conceito, então pode ser mais robusta, mas aqui está a ideia:

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

Makefile:

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

all: fcat
.PHONY:all

Meus resultados de temporização com 12 encadeamentos mostram tempos decorridos de 0,2 segundos em comparação com 2,3 segundos para cat (mediana de três execuções cada, com cache quente, em 48 arquivos totalizando 138M).

    
por 22.09.2015 / 15:49