неудачное использование popen() в C?

Я могу запустить следующую команду

xwd -root | xwdtopnm | pnmtojpeg > screen.jpg

в терминале под Linux, и он создаст скриншот моего текущего экрана.

Я пытаюсь сделать следующее с кодом:

#include <stdio.h>
#include <stdlib.h>
int main()
{
   FILE *fpipe;
   char *command="xwd -root | xwdtopnm | pnmtojpeg";
   char line[256];

   if ( !(fpipe = (FILE*)popen(command,"r")) )
   {  // If fpipe is NULL
      perror("Problems with pipe");
      exit(1);
   }

   while ( fgets( line, sizeof line, fpipe))
   {
      //printf("%s", line);
      puts(line);
   }
   pclose(fpipe);
}

затем я компилирую и запускаю программу ./popen > screen.jpg, но результирующий файл screen.jpg не распознается. Как я могу сделать это, чтобы я мог правильно передать свою программу?


person Community    schedule 08.05.2009    source источник


Ответы (4)


Вы не должны использовать fgets и puts для работы с двоичными данными. fgets остановится всякий раз, когда увидит новую строку. Хуже того, puts выводит дополнительные символы новой строки, а также останавливается всякий раз, когда сталкивается с символом \0. Вместо этого используйте fread и fwrite.

person Laurence Gonsalves    schedule 08.05.2009
comment
Извините, я имел в виду fread и fwrite, а не чтение и запись. (только что отредактировал мой ответ) - person Laurence Gonsalves; 08.05.2009

Функции fgets и puts не предназначены для использования с двоичными данными, такими как файлы изображений. Их следует использовать только со строками текста. В C строки заканчиваются нулевым байтом ('\0'). Поскольку на самом деле это просто ноль, он может появиться где угодно в двоичном файле. Предположим, что line[] заполнен 256 символами данных. Когда вы вызываете puts, функция считывает массив до тех пор, пока не встретит нулевой байт, затем предполагает, что он достиг конца строки, и останавливается. Поскольку в двоичном файле нулевой байт может появиться где угодно (а не только в конце массива), функция puts может легко не распечатать разделы ваших данных.

На вашем месте я бы изучил функции fread и fwrite и использовал их вместо них. На машине с Linux вы должны просто набрать man 3 fread, чтобы прочитать документацию по обеим функциям.

person Stephen Van Dahm    schedule 08.05.2009

Для тех, у кого возникла такая же проблема, я, наконец, заставил ее работать, используя системные вызовы чтения/записи Unix:

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

//writes to an output file test.jpg directly
int main()
{
    FILE *fpipe;
    char *command="xset b off  && xwd -root | xwdtopnm 2> /dev/null | pnmtojpeg";
    char buff[256];
    size_t result_write;
    size_t result_read;

    if ( !(fpipe = (FILE*)popen(command,"r")) )
    {  // If fpipe is NULL
        perror("Problems with pipe");
        exit(1);
    }

    int dest_fd = open("test.jpg",  O_RDWR|O_TRUNC|O_CREAT, S_IRUSR|S_IWUSR );
    int fd = fileno(fpipe);
    while((result_read = read(fd, buff, sizeof(char)*256))>0){  
        if(result_read < 0){
            perror("Problem while reading.\n");
            exit(1);
        }
        result_write = write(dest_fd, buff, sizeof(char)*256);
        if(result_write < 0){
            perror("Probelms writing to outputfile.\n");
            exit(1);
        }   
    }
    close(dest_fd);     
   pclose(fpipe);
}
person Community    schedule 08.05.2009
comment
Пока вы следите за тем, чтобы не выполнять буферизованный ввод-вывод (fread, fscanf, fwrite, fprintf,...) и небуферизованный ввод-вывод (чтение, запись,...) в одном и том же файле, все должно быть в порядке. Не понимаю, почему вы просто не использовали fread и fwrite. - person ephemient; 13.05.2009

Не тестируя ваш код, я сомневаюсь, что "xwd -root | xwdtopnm | pnmtojpeg" работает как аргумент для C - Pipe.

В любом случае я бы не стал использовать программу C для такой проблемы. Вместо этого используйте простой сценарий Bash.

person Chris    schedule 08.05.2009
comment
Я не могу использовать сценарий Bash, это просто тест. Мне нужен какой-то программный способ делать снимки экрана в X11, и это лучшее, что я мог придумать, не просматривая весь исходный код X11. - person ; 08.05.2009