Nano Hash - криптовалюты, майнинг, программирование

Реализация перенаправления ввода/вывода в оболочке Linux с использованием C

Я пытаюсь создать базовую оболочку для Linux с помощью C. Я заставил ее работать, пока не попытался выполнить перенаправление вывода, и она просто все уничтожила. Когда я запускаю этот код, он переходит прямо к случаю fork() по умолчанию. Понятия не имею почему. Если я избавлюсь от цикла for в дочернем процессе, он сработает, но даже с циклом for я не понимаю, почему дочерний процесс даже не запускается. Если я помещу оператор печати вверху дочернего процесса, он не будет напечатан.

Когда я запускаю это в командной строке, я получаю подсказку и набираю что-то вроде «ls», что работало до того, как я добавил цикл for, но теперь я просто получаю «% я здесь», и если я нажимаю ввод, он просто продолжает выдавать мне та самая линия. Моя цель - набрать «ls > output» и заставить его работать. Я думаю, что перенаправление ввода работает, но, честно говоря, я даже особо не играл с ним, потому что совершенно не понимаю, что происходит с перенаправлением вывода. Буду очень признателен за любую помощь, я потратил 4 часа на то же самое, что и 15 строк, пытаясь заставить это работать.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#define HISTORY_COUNT 20
char *prompt = "% ";

int
main()
{
int pid;
//int child_pid;
char line[81];
char *token;
char *separator = " \t\n";
char **args;
char **args2;
char **args3;
char cmd[100];
char *hp;
char *cp;
char *ifile;
char *ofile;
int check;
int pfds[2];
int i;
int j;
int current = 0;
int p = 0;
//int check;
char *hist[HISTORY_COUNT];
//char history[90];
//typedef void (*sighandler_t) (int);

args = malloc(80 * sizeof(char *));
args2 = malloc(80 * sizeof(char *));

signal(SIGINT, SIG_IGN);

while (1) {
    fprintf(stderr, "%s", prompt);
    fflush(stderr);

    if (fgets(line, 80, stdin) == NULL)
        break;


    // split up the line

    i = 0;
    while (1) {
        token = strtok((i == 0) ? line : NULL, separator);
        if (token == NULL)
            break;
        args[i++] = token;

             /* build command array */

    }
    args[i] = NULL;

    if (i == 0){
        continue;
    }

    // assume no redirections
    ofile = NULL;
    ifile = NULL;

    // split off the redirections
    j = 0;
    i = 0;
    while (1) {        //stackoverflow.com/questions/35569673
        cp = args[i++];
        if (cp == NULL)
            break;

        switch (*cp) {
        case '<':
            if (cp[1] == 0)
                cp = args[i++];
            else
                ++cp;
            ifile = cp;
            break;

        case '>':
            if (cp[1] == 0)
                cp = args[i++];
            else
                ++cp;
            ofile = cp;
            break;
    case '|':

        if(cp[1] ==0){
           cp = args[i++];
           if(pipe(pfds) == -1){
               perror("Broken Pipe");
               exit(1);
           }
       p = 1;
    }
    else{ 
       ++cp;

    }
       break;

        default:
            args2[j++] = cp;
        args3[cp++] = cp
            break;
        }
    }
    args2[j] = NULL;
    if (j == 0)
        continue;

    switch (pid = fork()) {
        case 0:
            // open stdin
            if (ifile != NULL) {
                int fd = open(ifile, O_RDONLY);

                if (dup2(fd, STDIN_FILENO) == -1) {
                    fprintf(stderr, "dup2 failed");
                }

                close(fd);
            }


            // open stdout
            if (ofile != NULL) {
                // args[1] = NULL;
                int fd2;


                if ((fd2 = open(ofile, O_WRONLY | O_CREAT, 0644)) < 0) {
                    perror("couldn't open output file.");
                    exit(0);
                }

                // args+=2;
                printf("okay");
                dup2(fd2, STDOUT_FILENO);
                close(fd2);
            }


        if(p == 1){        //from stackoverflow.com/questions/2784500
        close(1);
        dup(pfds[1]);
        close(pfds[0]);
        execvp(args2[0], args2);
        break;
        }


        if(strcmp(args2[0], "cd") == 0){            //cd command
            if(args2[1] == NULL){
                fprintf(stderr, "Expected argument");
            }
            else{

            check = chdir(args2[1]);

        if(check != 0){
            fprintf(stderr,"%s",prompt);

            }
           }
        break;
        }

        execvp(args2[0], args2);        /* child */
        signal(SIGINT, SIG_DFL);
        fprintf(stderr, "ERROR %s no such program\n", line);
        exit(1);
        break;

    case -1:
        /* unlikely but possible if hit a limit */
        fprintf(stderr, "ERROR can't create child process!\n");
        break;

    default:
        //printf("am I here");
    if(p==1){
        close(0);
        dup(pfds[0]);
        close(pfds[1]);
        //execvp();
    }
        wait(NULL);
        //waitpid(pid, 0, 0);
    }
}

exit(0);

}

23.02.2016

  • printf буферизуется строкой. Нет новой строки, нет вывода. Так что заканчивайте свои строки printf новой строкой. В противном случае данные отладки могут сбивать с толку, поскольку кажется, что код printf не был выполнен, хотя на самом деле он был выполнен, а только этот стандартный вывод не был очищен. 23.02.2016
  • @kaylum Правда? Я понятия не имел, я всегда думал, что \t и \n предназначены только для форматирования, поэтому это было более читабельно. 23.02.2016
  • @kaylum Спасибо, теперь я немного обрел рассудок. 23.02.2016

Ответы:


1

Я добавил отдельный проход аргумента для захвата и запоминания перенаправлений ввода-вывода и удаления их из списка аргументов, переданного дочернему элементу.

Вот исправленный код [пожалуйста, простите за неуместную очистку стиля]:

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

char *prompt = "% ";

int
main()
{
    int pid;
    //int child_pid;
    char line[81];
    char *token;
    char *separator = " \t\n";
    char **args;
    char **args2;
    char *cp;
    char *ifile;
    char *ofile;
    int i;
    int j;
    int err;
    //int check;
    //char history[90];
    //typedef void (*sighandler_t) (int);

    args = malloc(80 * sizeof(char *));
    args2 = malloc(80 * sizeof(char *));

    //signal(SIGINT, SIG_IGN);

    while (1) {
        fprintf(stderr, "%s", prompt);
        fflush(stderr);

        if (fgets(line, 80, stdin) == NULL)
            break;

        // split up the line
        i = 0;
        while (1) {
            token = strtok((i == 0) ? line : NULL, separator);
            if (token == NULL)
                break;
            args[i++] = token;              /* build command array */
        }
        args[i] = NULL;
        if (i == 0)
            continue;

        // assume no redirections
        ofile = NULL;
        ifile = NULL;

        // split off the redirections
        j = 0;
        i = 0;
        err = 0;
        while (1) {
            cp = args[i++];
            if (cp == NULL)
                break;

            switch (*cp) {
            case '<':
                if (cp[1] == 0)
                    cp = args[i++];
                else
                    ++cp;
                ifile = cp;
                if (cp == NULL)
                    err = 1;
                else
                    if (cp[0] == 0)
                        err = 1;
                break;

            case '>':
                if (cp[1] == 0)
                    cp = args[i++];
                else
                    ++cp;
                ofile = cp;
                if (cp == NULL)
                    err = 1;
                else
                    if (cp[0] == 0)
                        err = 1;
                break;

            default:
                args2[j++] = cp;
                break;
            }
        }
        args2[j] = NULL;

        // we got something like "cat <"
        if (err)
            continue;

        // no child arguments
        if (j == 0)
            continue;

        switch (pid = fork()) {
        case 0:
            // open stdin
            if (ifile != NULL) {
                int fd = open(ifile, O_RDONLY);

                if (dup2(fd, STDIN_FILENO) == -1) {
                    fprintf(stderr, "dup2 failed");
                }

                close(fd);
            }

            // trying to get this to work
            // NOTE: now it works :-)
            // open stdout
            if (ofile != NULL) {
                // args[1] = NULL;
                int fd2;

                //printf("PLEASE WORK");
                if ((fd2 = open(ofile, O_WRONLY | O_CREAT, 0644)) < 0) {
                    perror("couldn't open output file.");
                    exit(0);
                }

                // args+=2;
                printf("okay");
                dup2(fd2, STDOUT_FILENO);
                close(fd2);
            }

            execvp(args2[0], args2);        /* child */
            signal(SIGINT, SIG_DFL);
            fprintf(stderr, "ERROR %s no such program\n", line);
            exit(1);
            break;

        case -1:
            /* unlikely but possible if hit a limit */
            fprintf(stderr, "ERROR can't create child process!\n");
            break;

        default:
            //printf("am I here");
            wait(NULL);
            //waitpid(pid, 0, 0);
        }
    }

    exit(0);
}

ОБНОВЛЕНИЕ:

Если ты еще здесь, не мог бы ты помочь мне с созданием трубки?

Конечно. Он слишком большой, чтобы публиковать здесь. См.: http://pastebin.com/Ny1w6pUh.


Вау, ты создал все 3300 строк?

Да.

Я позаимствовал xstr из другого моего SO-ответа [с исправлением и улучшением]. dlk был новым, но я делаю многие из них, так что это было легко. В основном это был новый код.

Но... Он был составлен из фрагментов/концепций, которые я делал раньше: tgb, FWD, BTV, sysmagic. Обратите внимание, что все члены структуры для struct foo имеют префикс foo_ [стандартный для меня]. Макрос «хитрость» с DLHDEF и DLKDEF для имитации наследования/шаблонов — это тоже то, что я делаю [при необходимости].

Многие функциональные переменные повторно используют мой стиль: idx для индексной переменной [я бы никогда не использовал i/j, а скорее xidx/yidx], cp для указателя на символ, cnt для подсчета, len для длины байта и т. д. Таким образом, мне не нужно "думать" о мелочах [тактике] и может сосредоточиться на стратегии.

Вышеупомянутый idx et. др. для меня «фирменный стиль». Это не обязательно лучше [или хуже], чем другие. Это связано с тем, что я начал использовать C, когда компоновщик/загрузчик мог обрабатывать только 8-символьные символы, поэтому краткость была ключевым моментом. Но я привык к более коротким именам. Подумайте, что понятнее/лучше:

for (fooidx = 0;  fooidx <= 10;  ++fooidx)

Or:

for (indexForFooArray = 0;  indexForFooArray <= 10;  ++indexForFooArray)

Я использую do { ... } while (0), чтобы избежать if/else лестниц много. Это называется «одноразовым» циклом. Это считается «спорным», но, по моему опыту, это делает код чище. Лично я никогда не встречал [более стандартного] использования цикла do/while, которое нельзя было бы сделать проще/лучше с циклом while или for — YMMV. На самом деле, в ряде языков вообще нет do/while.

Кроме того, я использую строчные буквы, если только это не #define [или enum], которые всегда заглавные. То есть я использую "змеиный футляр" (например, fooidx), а не "верблюжий горб" (например, indexForFooArray).

Файл .proto, содержащий прототипы функций, создается автоматически. Это значительно экономит время. Примечание. Убедитесь, что у вас есть как минимум версия 2 по внешней ссылке, так как в Makefile обнаружена ошибка. make clean сотрет .proto. v2 этого не сделает

За эти годы я выработал свой собственный стиль. Оказывается, стиль ядра Linux был «позаимствован у меня». На самом деле нет :-) Мой был первым. Но... Параллельно придумали нечто, совпадающее с моим на 99%: /usr/src/kernels/whatever_version/Documentation/CodingStyle.

Соответствие заданному стилю [собственному] является ключевым. Для данной функции вам не нужно беспокоиться о том, как вы назовете переменные, какой отступ или пустые строки вы будете использовать.

Это помогает читателю/новому разработчику. Они могут прочитать несколько функций, увидеть стиль в игре, а затем работать быстрее, потому что все функции имеют схожий стиль.

Все это позволяет «работать быстрее» и при этом получать качественный код с первого раза. Я также достаточно опытен.

Кроме того, мои комментарии к коду сосредоточены на «намерении». То есть, что вы хотите, чтобы код делал в реальных условиях. Они должны отвечать на вопрос "что/почему", а код - на вопрос "как".

23.02.2016
  • Вау, я так давно не видел хорошего кода, что забыл, насколько ужасным был мой. Это работает отлично спасибо! Впервые я увидел этот встроенный условный синтаксис if/ternary. 23.02.2016
  • Пожалуйста! Помимо повторного отступа [с indent] и упрощения, когда/где это необходимо, я пытался сохранить как можно больше исходного кода и стиля, чтобы он не выглядел для вас совершенно чуждым. 23.02.2016
  • Если ты еще здесь, не мог бы ты помочь мне с созданием трубки? У меня есть идея, что это работает, просто нужна помощь. 25.02.2016
  • Конечно! Ух ты! Трубка. Для этого понадобится кофе (в Старбаксе) :-) Вообще-то, ты поймал меня на выходе. Итак, я вернусь через пару часов. Между тем, возможно, вы можете отредактировать свой пост и добавить свои новые мысли и / или код внизу. Кстати, я должен думал о том, как сделать трубы [по этому поводу был еще один ТАК вопрос]. Я почти разместил [здесь] более продвинутую версию, которая заложила бы основу для этого 25.02.2016
  • Я пошел дальше и отредактировал его. Я попытался удалить некоторые другие вещи, которые мне нужны, чтобы их было легче читать. Я думаю, главное, что мне нужно, это как-то получить все после '|' в отдельные аргументы (отсюда args3[]), поэтому я могу заставить родителя выполнять их. 25.02.2016
  • @Pongjazzle Вас ни в коем случае не бросили. Вот обновленная версия: pastebin.com/Ny1w6pUh Она [сейчас] слишком велика для публикации здесь. Он делает '&', '|' и ';' разбор. Это немного отличается от базового примера. Не стесняйтесь задавать столько вопросов, сколько хотите 02.03.2016
  • Вау, ты создал все 3300 строк? 03.03.2016
  • Новые материалы

    Кластеризация: более глубокий взгляд
    Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

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

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

    Как я автоматизирую тестирование с помощью Jest
    Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

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

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..