2015-05-22 3 views
1

Например, я код:Как передать переменную команде оболочки в C?

fp = popen("wc -l < myfile", "r"); 

Но myfile должно быть имя любого файла, который разбирается в этом проекте. Это может быть файл abc.txt или 123.txt или xy.txt т.д.

Тогда я хочу, чтобы получить выход выполнения этого wc -l < myfile. Но проблема в том, что я не знаю, какая функция в C может помочь мне разобрать имя myfile на эту команду оболочки, и я также могу получить вывод. Может ли кто-нибудь дать мне несколько предложений?

Редактировать: Файл, который я хочу прочитать, очень большой. Я хочу прочитать его данные в array.I не могу использовать список для его хранения, потому что слишком медленно найти конкретные данные в списке. Проблема в том, что, если я использую один размерный массив для malloc() памяти для массива, на ноутбуке недостаточно свободного пространства. Поэтому я планирую использовать двухмерный массив для его хранения. Поэтому я должен получить количество строк в файле, а затем решить размер каждого размера в этом массиве через log.

Спасибо за все ответы. Этот проект посвящен чтению двух файлов. Первый файл намного больше, чем второй файл. Второй файл выглядит следующим образом:

1 13 0 
2 414 1 
3 10 0 
4 223 1 
5 2 0 

Третье число в каждой строке называется «ID». Например, число «1» имеет идентификатор 0, номер «2» имеет идентификатор 1, номер «3» имеет идентификатор «0». (Не обращайте внимания на среднюю Num в каждой строке) И первый файл, как:

1 1217907 
1 1217908 
1 1517737 
1 2 
2 3 
2 4 
3 5 
3 6 

Если каждый Num в первом файле имеет идентификатор «0», я должен хранить обоих NUM в каждой строке в массив структуры данных. Например, мы можем видеть, что Num «1» имеет идентификатор «0» во втором файле, так что мне нужно хранить:

1 1217907 
1 1217908 
1 1517737 
1 2 

из моего первого файла в массив структур данных. Число «2» имеет идентификатор «1», но число «3» имеет идентификатор «0», а число «4» имеет идентификатор «1», поэтому необходимо сохранить: 2 3, но не хранить 2 4 из моего первого файла. Вот почему мне нужно использовать массив для хранения двух файлов. Если я использую два массива для их хранения, я могу проверить, является ли идентификатор этого номера «0» быстрым в массиве, принадлежит к второму файлу, потому что с помощью массива быстро найти определенные данные, индекс может быть непосредственно значением num.

+0

«Недостаточно непрерывного пространства памяти» на самом деле не имеет смысла. Современные операционные системы используют пространство виртуальной памяти, которое намного больше физической памяти.Даже если у вас недостаточно физической памяти, это не имеет значения. –

+0

Даже если файл 20G? – beasone

+0

Большинство моих одноклассников не смогли выделить достаточное смежное пространство памяти. Они сказали мне, что их программа врезалась на компьютер, который используется для тестирования своих программ. Они сказали, что на компьютере недостаточно места для памяти, потому что он старый ... – beasone

ответ

1

Я думаю, вам нужно использовать snprintf(), чтобы сгенерировать строку, которая будет передана в popen(), а затем вы можете позвонить popen() с этой строкой.

Псевдо-код

char buf[32] = {0}; 
snprintf(buf, 32, "wc -l < %s", myfile); 
fp = popen(buf, "r"); 

EDIT

Чтобы заставить его работать на любой длине myfile

int len = strlen(myfile) + strlen("wc -l < ") + 1; 
char *buf = malloc(len); 
snprintf(buf, len, "wc -l < %s", myfile); 
fp = popen(buf, "r"); 

... 

free(buf); 

Примечание: Как упомянуто Ed Heal in the comment, то 32 здесь используется здесь всего лишь демо цель.Вы должны выбрать длину своего временного массива в зависимости от длины строки, хранящейся на myfile, плюс обязательные символы, плюс нулевой ограничитель, очевидно.

+0

Это хороший ответ - используйте строки формата! – NBartley

+0

Как долго 'myFile' - Надеюсь, что это не слишком долго –

+0

@NatashaDutta - Зачем использовать оболочку и т. Д. Для этого? Кажется, много накладных расходов –

2

Забудьте popen - сделать это самостоятельно

т.е.

FILE *f = fopen(argv[1], "r"); 
int lines = 0; 
int ch; 
while ((ch = fgetc(f)) != EOF) { 
    if (c == '\n') lines++; 
} 

EDIT - Как плакат хочет, чтобы загрузить весь файл в память

Добавить проверку на наличие ошибок

FILE *f = fopen(argv[1], "r"); 
struct stat size; 
fstat(fileno(f), &size); 

char buf = malloc(size.st_size) 
fread(buf, size.st_size, 1, f); 
fclose(f); 
+0

Это правильное решение проблемы примера, но не для общей проблемы. – jxh

+0

Какова общая проблема - я думал, что она читает числовые строки в файле - зачем создавать оболочку и т. Д. –

+0

Общая проблема заключается в отправке произвольного имени файла в команду оболочки, вызванную 'popen()'. Команда не обязательно должна быть 'wc -l <'. – jxh

2

Если вы не собираетесь делать это самостоятельно (без оболочки), то вы должны, по крайней мере, передать имя файла таким образом, чтобы оболочка только когда-либо интерпретировала его как данные, а не код, чтобы избежать потенциальных инцидентов безопасности.

setenv("filename", "myfile");   /* put filename in the environment */ 
fp = popen("wc -l <\"$filename\"", "r"); /* check it from your shell script */ 
+0

Принимаем ли мы, что 'wc' делает то, что мы думаем. Подумайте, что это может быть сценарий оболочки, который он раньше на пути ?! –

+0

Это умный и уверенный, но он не реентерабелен. – jxh

+0

@jxh, действительно; в идеальном мире можно было бы сделать 'setenv()' внутри подпроцесса после форкирования и до того, как выполнить оболочку, тем самым изолируя ее от остальной части процесса. Позор, что 'popen()' не разработан с учетом использования (в отличие от вашей серии 'execv *' вызовов, которые _do_ позволяют передавать явные переменные среды). –

2

Все ниже код тестировался. Если я найду время для тестирования, я удалю это предостережение.

Вы можете создать свою собственную обертку до popen(), чтобы создать произвольную команду.

FILE * my_popen (const char *mode, const char *fmt, ...) { 
    va_list ap; 
    int result = 511; 

    for (;;) { 
     char buf[result+1]; 

     va_start(ap, fmt); 
     result = vsnprintf(buf, sizeof(buf), fmt, ap); 
     va_end(ap); 

     if (result < 0) return NULL; 
     if (result < sizeof(buf)) return popen(buf, mode); 
    } 

    /* NOT REACHED */ 
    return NULL; 
} 

Затем, вы можете назвать это так:

const char *filename = get_filename_from_input(); 
FILE *fp = my_popen("r", "%s < %s", "wc -l", filename); 
if (fp) { 
    /* ... */ 
    pclose(fp); /* make sure to call pclose() when you are done */ 
} 

Здесь мы предполагаем, что get_filename_from_input() преобразует входную строку имя файла в нечто безопасное для оболочки потребить.


Это довольно сложное (и подвержены ошибкам), чтобы надежно исправить имя файла во что-то оболочка будет лечить безопасно. Безопаснее открыть файл самостоятельно. Однако после этого вы можете подать файл команде, а затем зачитать полученный результат. Проблема в том, что вы не можете использовать popen(), так как стандарт popen() поддерживает только однонаправленную связь. & кинжал;

& dagger; Some variations of popen() exist that support bidirectional communication.

FILE * my_cmd_open (const char *cmd) { 
    int s[2], p, status, e; 
    if (socketpair(AF_UNIX, SOCK_STREAM, 0, s) < 0) return NULL; 
    switch (p = fork()) { 
    case -1: e = errno; close(s[0]); close(s[1]); errno = e; return NULL; 
    case 0: close(s[0]); dup2(s[1], 0); dup2(s[1], 1); dup2(s[1], 2); 
      switch (fork()) { 
      case -1: exit(EXIT_FAILURE); 
      case 0: execl("/bin/sh", "-sh", "-c", cmd, (void *)NULL); 
        exit(EXIT_FAILURE); 
      default: exit(0); 
      } 
    default: for (;;) { 
       if (waitpid(p, &status, 0) < 0 && errno == EINTR) continue; 
       if (WIFEXITED(status) && WEXITSTATUS(status) == 0) break; 
       close(s[0]); close(s[1]); errno = EPIPE; 
       return NULL; 
      } 
    } 
    close(s[1]); 
    return fdopen(s[0], "r+"); 
} 

Чтобы эффективно прочитать весь файл в память, вы можете использовать mmap().

void * mmap_filename (const char *filename, size_t *sz) { 
    int fd = open(filename, O_RDONLY); 
    if (fd < 0) return NULL; 
    struct stat st; 
    if (fstat(fd, &st) < 0) { 
     close(fd); 
     return NULL; 
    } 
    *sz = st.st_size; 
    void *data = mmap(NULL, *sz, PROT_READ, MAP_PRIVATE, fd, 0); 
    close(fd); 
    return data != MAP_FAILED ? data : NULL; 
} 

Затем, вы можете назвать это так:

size_t sz; 
void *data = mmap_filename(filename, &sz); 
if (data) { 
    /* ... */ 
    munmap(data, sz); 
} 

В примере кода выше отображает весь файл сразу. Однако API mmap() позволяет отображать части файла с определенного смещения в файл.

+0

Все еще открыт для уязвимостей инъекций оболочки (т. Е. 'Myfile', содержащий' '$ (rm -rf /) '', для повторного использования примера). –

+0

** Все еще ** открыта для уязвимостей инъекций оболочки, просто с немного отличающимся механизмом эвакуации. –

+0

@CharlesDuffy: Вы имеете в виду, если строка имени файла содержит одинарные кавычки. – jxh

Смежные вопросы