Чтение команд из строки cmd и их выполнение в C

Я пишу программу, которая принимает пользовательский ввод из командной строки (команды linux/unix) и выполняет их в своей программе.

Мои шаги на данный момент:

  1. Запросить у пользователя количество введенных команд
  2. Fork() для создания дочернего процесса
  3. Вывод дочернего PID и родительского PID
  4. Разрешить пользователю вводить каждую команду, считывать каждый ввод в индекс argv
  5. Используйте execv для запуска каждой команды внутри argv

Основная проблема заключается в том, что при выполнении он просто выполняет «bin/ls/» в команде execv.

Вот пример вывода при запуске моей программы:

Введите количество команд: 2
PID дочернего элемента – 3487. PID родителя – 3485
Введите команду UNIX: ls
Введите команду UNIX : -al

СПИСОК ФАЙЛОВ, КАК ПРИ ВВОДЕ "LS" В ЛИНИИ CMD

Процесс завершен.


И вот мой исходный код:

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

void main(int argc, char *argv[20])
{
        int pid;
        int num = 0;

        printf("Enter number of commands: ");
        scanf("%d", &argc);

        pid = fork();

        if(pid == 0)
        {
                printf("Child's PID is %d. Parent's PID is %d\n", (int)getpid(), (int)getppid());

                for(num=0; num < argc; num++)
                {
                        printf("Enter a UNIX command: ");
                        scanf("%s", argv[num]);
                }

                argv[num+1] = 0;

                execv("bin/ls/", argv);
        }
        else
        {
                wait(pid);
                printf("Process Complete.\n");
                exit(0);
        }
}

person Baelix    schedule 21.09.2012    source источник
comment
Вы всегда должны проверять коды возврата из системных вызовов. Когда вы это сделаете, вы увидите, что ваш execv() потерпит неудачу.   -  person Duck    schedule 22.09.2012
comment
Мне не очень нравится пользовательский интерфейс, который вы выбрали для реализации (или получили указание реализовать). Компьютеры умеют считать; Я не должен. Кроме того, если я наберу /usr/bin/perl в качестве команды для выполнения, я не буду счастлив, если вместо этого ваша оболочка выполнит /bin/ls. Кажется, вы принимаете количество аргументов, а не количество команд.   -  person Jonathan Leffler    schedule 22.09.2012


Ответы (3)


Во-первых, вы определяете char* argv[20] в main, что не очень хорошая идея. Если вы когда-нибудь передадите более 20 аргументов, вы превысите границы массива.

Во-вторых, вы пытаетесь прочитать строку с scanf("%s", argv[num]) в адресное пространство, которое, насколько я могу судить, не инициализировано.

Массив «строк» ​​argv[] инициализируется ОС при вызове вашей программы, и если вы не передаете никаких аргументов своей программе, у вас не будет никаких «строк», что означает, что вы будете записывать в случайную память, которая вы можете не владеть.

Если вы действительно хотите загрузить свои команды так, как вы это делаете сейчас, попробуйте следующее:

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

void main(int argc, char *argv[])
{
    int pid;
    int num = 0;
    int argc2 = 0;
    char* argv2[20]; // argv2 will point inside of buffer for convenience.
    char* buffer[2000]; // note each array has a limit of 100 characters.

    printf("Enter number of commands: ");
    scanf("%d", &argc2);

    pid = fork();

    if(pid == 0)
    {
            printf("Child's PID is %d. Parent's PID is %d\n", (int)getpid(), (int)getppid());

            for(num=0; num < argc2 && num < 20; num++) // your array is 20 long
            {
                    argv2[num] = &buffer[num * 100];
                    printf("Enter a UNIX command: ");
                    scanf("%s", argv2[num]);
            }

            argv[num] = 0; // no need to add + 1 because the for loop did already anyway.

            execv("Assignments/ls", argv2);
    }
    else
    {
            wait(pid);
            printf("Process Complete.\n");
            exit(0);
    }
}

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

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

void main(int argc, char *argv[])
{
    int pid;
    int num = 0;

    printf("You entered %d commands: \n", argc);

    for (num = 0; num < argc; ++num)
    {
        printf("\t%s\n", argv[num]);
    }

    pid = fork();

    if(pid == 0)
    {
            printf("Child's PID is %d. Parent's PID is %d\n", (int)getpid(), (int)getppid());

            execv("Assignments/ls", &argv[1]);
    }
    else
    {
            wait(pid);
            printf("Process Complete.\n");
            exit(0);
    }
}
person nonsensickle    schedule 21.09.2012
comment
Ваше назначение argv2[num] = buffer + num * 100 наверняка вызовет ошибку. Вы назначаете целое число указателю char. - person Baelix; 22.09.2012
comment
@Baelix Я добавлял целое число к char*, а затем присваивал char*, что должно быть хорошо. Я изменил его на argv2[num] = &buffer[num * 100]; для ясности. Спасибо. - person nonsensickle; 22.09.2012
comment
Ах, ладно, теперь это имеет больше смысла. Я вернулся и реструктурировал свой код, не используя аргументы argv[] и argc, переданные в main. Когда мой профессор (плохо) объяснил это, она сказала нам передать аргументы, используя argv[]. Я думаю, она хотела сказать, что нам нужно передать аргументы НАШЕЙ функции в execv, вторым параметром которого является argv[]. Тем не менее, решил мою проблему. Большое спасибо за помощь! - person Baelix; 22.09.2012

Одна конкретная проблема, с которой сталкивается ваш код, заключается в том, что вы должны передать argv[idx] в качестве аргумента для exec. Вы передаете массив указателей на символы, передавая argv.

Также имейте в виду, что argc содержит полное количество аргументов, и это полное количество включает саму программу. argv[0] содержит имя программы, которой вы передаете аргументы. Я не вижу, чтобы это отражалось в вашем цикле for. То есть вы обрабатываете свою собственную программу и запускаете ее.

То, как я написал это, состоит в том, чтобы пройти через argv через некоторое время (или for, если хотите), используя переменную int, например int idx=0;, пока я не найду указатель argv[idx], который равен нулю.

Если бы, например, у вас было три аргумента, argc было бы равно 4, а argv[3] был бы вашим последним аргументом для обработки. argv[4] будет нулевым.

Основываясь на некоторых полученных вами ответах, вот обсуждение execv и fork.

person octopusgrabbus    schedule 21.09.2012
comment
Итак, по сути, я бы запустил некоторое время (или цикл for), как показано ниже? while(argv[idx] != NULL){ execv("/bin/ls", argv[idx]); Или что-то в этом роде? - person Baelix; 22.09.2012
comment
Что бы я сделал, так это построил эту программу поэтапно или, по крайней мере, ввел логику #define DEBUG, которая вместо фактического запуска команд распечатывала бы их. Кроме того, я считаю, что ответ, который вы получили о вилке, стоит изучить. Я не помню, чтобы использовал execv, но я писал такой код много-много лет назад. Посмотрите на ссылку, которую я дал в своем ответе, и проверьте, что вам дали в других ответах. Эти ответы уловили то, что я пропустил. Я просто увидел и ответил на первое, что бросилось в глаза. - person octopusgrabbus; 22.09.2012

  1. У вас неправильная логика. использовать fork непосредственно перед execv
  2. вывести execv (вместе с fork) из цикла;
  3. 1-й аргумент execv - это путь к исполняемому бинарному файлу; 2-й - массив аргументов для передачи в двоичный файл. Верно ли, что у вас в текущем каталоге есть подкаталог с именем «Назначения», и этот каталог содержит исполняемый файл с именем «ls»? И, пожалуйста, внимательно прочитайте "man execv"

Обновление:

Пункты 1 и 2 выше не учитывать.

человек исполнитель:

   The execv(), execvp(), and execvpe()  functions  provide  an  array  of
   pointers  to  null-terminated  strings that represent the argument list
   available to the new  program.   The  first  argument,  by  convention,
   should  point  to the filename associated with the file being executed.
   The array of pointers must be terminated by a NULL pointer.
person Serge    schedule 21.09.2012
comment
Также wait() принимает переменную состояния int, а не pid. Если вы хотите перезаписать pid, передайте его как wait(&pid). - person Duck; 22.09.2012
comment
execv не входит в цикл for, как и fork(); 1-й аргумент - это путь, да. У меня есть папка с именем Assignments в текущем каталоге. Однако я не считаю, что это должен быть путь к местоположению исполняемого файла. Например, /bin/ls просто запрашивает список файлов в корневой папке, а не выполняет файл; эта команда отлично работает при вводе в командную строку. - person Baelix; 22.09.2012
comment
Да, извините, я неправильно посчитал фигурные скобки. Вы можете верить мне или нет. Вам лучше внимательно прочитать "man execv", пожалуйста :) - person Serge; 22.09.2012