Как получить длину стандартного ввода из трубы? эхо "привет" | ./get_stdin_size - PullRequest
0 голосов
/ 19 марта 2020

Я установил приложение, и его командная строка может делать:

  1. command -input 1.txt

  2. command < 1.txt

  3. echo "hello" | command

и что-то вывести. У меня нет исходного кода, и я тоже хочу реализовать это поведение.

То, что я пробовал:

#include <stdio.h>
#include <unistd.h>
int main(int argc, char* argv[]){
    if ((fseek(stdin, 0, SEEK_END), ftell(stdin)) > 0){
        rewind(stdin);
        printf("stdin has data\n");
        char buffer[100];
        fgets(buffer, sizeof buffer, stdin);
        printf("stdin data are: %s\n", buffer);
    }else{
        if (argc < 2){
            printf("no cmd arguments\n");
            return -1;
        }else{
            printf("command line argument: %s\n", argv[1]);
            FILE* fp = fopen(argv[1], "r");
            if (fp == NULL){
                printf("NULL fp pointer\n");
                return -1;
            }
            char a[100] = {0};
            fgets(a, sizeof a, fp);
            printf("first line of file: %s\n", a);
        }
    }
    return 0;
}

Но проблема в том, что pipe не доступны Так что ((fseek(stdin, 0, SEEK_END), ftell(stdin)) > 0) подходит не для всех случаев.

Одно из решений, о котором я думаю:

#include <stdio.h>
#include <unistd.h>
int main(int argc, char* argv[]){
    if (argc > 1){
        //open file with argv[1] as filename
        //read data from disk file
    }else{
        //read data from stdin
        if(stdin is file){
            //get file size
            //read data from stdin
        }else if(stdin is pipe){
            //get pipe size
            //read data from stdin
        }
    }
    return 0;
}

У меня 2 проблемы с этим кодом:

  1. Есть ли функция ispipe(), которая работает как isatty(fileno(stdin))? Мне нужно сказать, является ли stdin pipe.

  2. Как мне получить stdin размер / длину из трубы? Очевидно, я не могу использовать:

    fseek(stdin, 0, SEEK_END);
    long size = ftell(stdin));

Как отметил @Peter в комментарии, я не должен пытаться получить размер stdin из канала заранее, тогда как я узнаю это доходит до конца? Может ли кто-нибудь дать мне минимальный пример об этой «потоковой обработке»?

Ответы [ 2 ]

2 голосов
/ 19 марта 2020

Вы можете использовать системный вызов fstat(), чтобы определить, является ли стандартный ввод каналом (анонимным или именованным) или файлом (и, если это файл, найдите его размер):

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

int main(void) {
  struct stat s;

  if (fstat(STDIN_FILENO, &s) < 0) {
    perror("fstat");
    return EXIT_FAILURE;
  }

  switch (s.st_mode & S_IFMT) {
  case S_IFIFO:
    puts("standard input is a pipe.");
    break;
  case S_IFREG:
    printf("standard input is a file that is %ld bytes in size.\n",
           (long)s.st_size);
    break;
  case S_IFCHR:
    if (isatty(STDIN_FILENO)) {
      puts("standard input is a terminal.");
    } else {
      puts("standard input is a character device.");
    }
    break;
  default:
    puts("standard input is something else.");
  }
  return 0;
}

Пример:

$ gcc testpipe.c
$ cat testpipe.c | ./a.out
standard input is a pipe.
$ ./a.out < testpipe.c    
standard input is a file that is 525 bytes in size.
$ ./a.out
standard input is a terminal.
2 голосов
/ 19 марта 2020

Единственный способ убедиться, что вы не получите больше данных из канала, это когда он закрыт (сигнал SIGPIPE).

Таким образом, как указано в комментариях, выделение / чтение права памяти с вызовами сложно, так как они могут быть бесконечными (например, /dev/random). Вы должны выдвинуть гипотезу или использовать дополнительные данные для обработки канала.

В зависимости от вашего варианта использования, эти стратегии могут быть следующими:

  1. Отправка длины данных на начало сообщения. Это может быть как: echo -e'\x05\x00\x00\x00Hello'|./myprog. С этой стратегией читать канал очень просто, но необходимо знать общий размер ввода перед тем, как начать его отправку.
  2. Выделение и чтение ограниченного количества данных / времени. Если вы получили PIPE_MAX_SIZE байт или ожидаете более TIMEOUT_PIPE, закройте канал и обработайте, возможно, неполное сообщение.
  3. Обработайте сообщение блок за блоком. Если ваше сообщение следует обычному шаблону, вы можете прочитать его таким образом и обрабатывать блоки последовательно, пока не достигнете конца сообщения. Это также позволяет вам отказаться от предыдущего буфера для чтения неограниченного количества данных, которые не помещаются в памяти.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...