Создайте md5sum в C

Я пытаюсь использовать команду md5sum в программе C, прямо сейчас я использую dirent.h, чтобы получить все файлы в папке, теперь я хочу получить все md5 из все эти файлы, я делаю это:

#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <syslog.h>
#include <string.h>
#include <dirent.h>

int main(void){

  char *word = ".gz";
  int i=0;
  char *word2 = ".";
  char *word3 = "..";
  unsigned int md5;
  DIR           *d;
  struct dirent *dir;
  d = opendir(".");
  if (d)  {

    while ((dir = readdir(d)) != NULL)
    {
      if((strstr(dir->d_name, word) == NULL) && (strcmp(dir->d_name, word2) != 0) && (strcmp(dir->d_name, word3)!= 0)) {
      md5 = system("md5sum dir->d_name");
      printf("The md5 of %s is %d\n", dir->d_name, md5);
      }
    }
  }
  return(0);
}

но когда я запускаю его, он говорит, например:

md5sum: dir-: No such file or directory
The md5 of ej1_signal.c is 256
md5sum: dir-: No such file or directory
The md5 of pipeL.c is 256

Не могли бы вы объяснить мне, почему это происходит? Спасибо !


person Hook    schedule 27.12.2014    source источник
comment
Я настоятельно рекомендую сначала прочитать документ о функции system.   -  person Cristian Ciupitu    schedule 27.12.2014
comment
Вы понимаете, что система вернет не значение md5, а код выхода команды?   -  person Iharob Al Asimi    schedule 27.12.2014
comment
Я один говорю, что на "md5sum dir->d_name"?   -  person E_net4 the curator    schedule 27.12.2014
comment
Я НАСТОЯТЕЛЬНО рекомендую использовать shell script для этих целей, анализ/выполнение команд оболочки через стандартный C .... в лучшем случае подвержен ошибкам, вам в значительной степени нужно быть экспертом в обоих языках. Всегда используйте тот инструмент, который подходит лучше всего, все остальное создаст головную боль, даже если вам кто-то поможет.   -  person specializt    schedule 27.12.2014
comment
попробуйте использовать snprintf для объединения md5sum %s с фактическим именем файла... и вам может понадобиться popen() вместо system(), если вы работаете в системе Posix... не уверен, что альтернатива win32   -  person technosaurus    schedule 27.12.2014
comment
Я изменил ваш код и опубликовал ответ, там я объясняю, как это сделать и почему.   -  person Iharob Al Asimi    schedule 27.12.2014


Ответы (4)


Функция system не возвращает вам то, что вы думаете. system используется для запуска команды, и когда эта команда завершается, она (обычно) завершается с кодом выхода. Это значение, которое вы поймали.

Вам нужен вывод команды, а не ее возвращаемое значение. Итак, что вам нужно, это popen, который позволяет вам запускать некоторую внешнюю команду и читать/записывать ее через канал. См. http://pubs.opengroup.org/onlinepubs/009695399/functions/popen.html например.

person Jean-Baptiste Yunès    schedule 27.12.2014
comment
Эй, спасибо, я этого не знал, системная штука, я прочитал это на форуме и подумал, что это сработает, я сделал это с помощью popen, спасибо :) - person Hook; 27.12.2014

system не возвращает вывод команды. Чтобы получить вывод команды, вам нужно создать процесс и связать стандартный поток вывода с дескриптором файла, из которого вы можете считывать данные в другом процессе. Пример того, как это сделать, вы можете найти на справочной странице pipe (раздел 2).

Другой вариант — использовать библиотеку, обеспечивающую реализацию MD5 (например, OpenSSL). На справочной странице EVP_DigestInit (раздел 3) приведен пример для этого.

Другая проблема заключается в том, что ваш код пытается вычислить дайджест d->d_name, а не файла, имя которого находится в d->d_name. Вы можете использовать sprintf или strncat с буфером подходящего размера (т.е. длина части статической строки md5sum плюс максимальный размер имени файла (обычно 256 байт, может варьироваться в зависимости от реализации библиотеки и файловой системы) плюс еще один байт для безопасного завершения строка (поскольку некоторые реализации могут сообщать о незавершенной строке в d->d_name)). Обратите внимание, что это не применяется, если вы используете библиотеку для расчета дайджеста, так как библиотека использует либо имя файла, либо вам необходимо передать содержимое файла библиотечной функции (например, EVP_DigestUpdate).

person Abrixas2    schedule 27.12.2014

Первая проблема заключается в том, что вы запускаете новый процесс оболочки, выполняющий "md5sum dir->d_name", что означает, что он выполняет md5 в «файле» с именем dir->d_name вместо использования значения, полученного из readdir.

Таким образом, вы можете добавить временную переменную и подготовить в ней команду перед запуском system.

limits.h для Linux, при необходимости измените его, чтобы получить максимальную длину пути

...
#include <linux/limits.h>

char temp[PATH_MAX];

тогда вместо

md5 = system("md5sum dir->d_name");

добавлять

strcpy(temp, "md5sum ");
strcat(temp, dir->d_name);
system(temp);

что касается другой проблемы (system не вернет строку md5), это отобразит md5 файла в каталоге. А можно просто удалить printf...

person Breaking not so bad    schedule 27.12.2014

В C нет команды для возврата вывода внешней команды, но существует popen, вы можете просто открыть команду как FILE * и прочитать ее вывод. Вот как вы можете это сделать, и все это объясняется в коде

#include <sys/types.h>
#include <sys/stat.h>

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

#include <dirent.h>

int main(void)
{
    DIR           *d;
    struct dirent *dir;

    d = opendir(".");
    if (d == NULL)
        return -1;

    while ((dir = readdir(d)) != NULL)
    {
        char        command[sizeof dir->d_name + 10];
        struct stat st;
        FILE       *pipe;

        if (stat(dir->d_name, &st) == -1)
            continue;
        /* check if the entry is a directory, md5sum does not work with them */
        if (S_ISDIR(st.st_mode) != 0)
            continue;
        /*
         * md5sum dir->d_name will pass `dir->d_name` as the argument to the md5sum command,
         * we need to build the command string, I like snprintf in this case
         */
        snprintf(command, sizeof command, "md5sum \"%s\"", dir->d_name);

        /*
         * Open the pipe, it will execute the new command in a new process (fork)
         * and create a pipe for communication with the current porcess
         */
        pipe = popen(command, "r");
        if (pipe != NULL)
        {
            char md5[33];

            /* read the md5 digest string from the command output */
            fread(md5, 1, sizeof md5 - 1, pipe);
            /* append a null terminator */
            md5[sizeof md5 - 1] = '\0';

            printf("The md5 of %s is %s\n", dir->d_name, md5);
        }
        /* close the pipe */
        pclose(pipe);
    }
    /* you should always call closedir() if opendir() succeded */
    closedir(d);

    return 0;
}
person Iharob Al Asimi    schedule 27.12.2014