Почему при выполнении команды ls вывод не производится? - PullRequest
0 голосов
/ 25 января 2020

Я пытаюсь написать программу, которая выполняет все, что указано в argv. Однако, когда я на самом деле запускаю execvp, он не выдает никаких выходных данных и не выдает никаких ошибок. Это моя программа.

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

int main (int argc, char * argv[]) {
    char * args[argc];
    for (int i = 0; i < argc - 1; ++i) 
        args[i] = strdup(argv[i+1]);
    args[argc-1] = malloc(1);
    *args[argc-1] = '\0';
    printf("%s\n", args[argc-1]);
    int rc = fork();
    if (rc == 0)
        execvp(args[0], args);
}

Вывод, полученный при запуске ./exec ls, представляет собой одну пустую строку. Однако, если я запускаю программу, жестко закодированную в массиве ls, она работает нормально. Почему эта программа не отображает содержимое каталога?

1 Ответ

1 голос
/ 25 января 2020

Делая вид, что необходимо продублировать аргументы и использовать fork(), и наблюдая, что если вы запустите:

./exec ls

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

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

int main (int argc, char *argv[])
{
    char *args[argc];
    for (int i = 0; i < argc - 1; ++i)
    {
        if ((args[i] = strdup(argv[i+1])) == NULL)
        {
            fprintf(stderr, "%s: failed to allocate memory for '%s'\n",
                    argv[0], argv[i+1]);
            exit(EXIT_FAILURE);
        }
    }
    args[argc-1] = NULL;

    int rc = fork();
    if (rc == 0)
    {
        execvp(args[0], args);
        fprintf(stderr, "%s: failed to execute '%s'\n", argv[0], args[0]);
        exit(EXIT_FAILURE);
    }
    else if (rc < 0)
    {
        fprintf(stderr, "%s: failed to fork\n", argv[0]);
        exit(EXIT_FAILURE);
    }
    return 0;
}

Родительский процесс должен действительно wait() для ребенка, прежде чем выйти из него. Код, вероятно, должен включать функцию для вывода содержимого списка аргументов. Вот пересмотренная версия кода, которая не использует strdup(), хотя она все еще работает. Он использует некоторый код, который доступен в моем репозитории SOQ (Вопросы о переполнении стека) на GitHub в виде файлов stderr.c и stderr.h в подкаталоге src / libsoq . Это упрощает обработку ошибок - отчеты об ошибках используют один вызов функции вместо нескольких строк кода, как раньше.

#include <unistd.h>
#include <sys/wait.h>
#include "stderr.h"

int main (int argc, char *argv[])
{
    err_setarg0(argv[0]);
    if (argc <= 1)
        err_usage("cmd [arg ...]");

    int rc = fork();
    if (rc == 0)
    {
        execvp(argv[1], &argv[1]);
        err_syserr("failed to execute '%s': ", argv[1]);
    }
    else if (rc < 0)
        err_syserr("failed to fork: ");

    int corpse;
    int status;
    while ((corpse = wait(&status)) > 0)
    {
        if (corpse != rc)
            err_remark("unexpected child PID %d status 0x%.4X\n", corpse, status);
    }
    if (WIFEXITED(status))
        rc = WEXITSTATUS(status);
    else if (WIFSIGNALED(status))
        rc = 128 + WTERMSIG(status);
    else
        rc = 255;   /* Something weird happened! */

    return rc;
}

Конечно, если вы не используете fork(), вам не нужно wait() либо. Это радикально уменьшает код:

#include <unistd.h>
#include "stderr.h"

int main (int argc, char *argv[])
{
    err_setarg0(argv[0]);
    if (argc <= 1)
        err_usage("cmd [arg ...]");
    execvp(argv[1], &argv[1]);
    err_syserr("failed to execute '%s': ", argv[1]);
}
...