Расширяемая команда C Minishell Печатная тарабарщина

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

$> echo hello $(echo world! ... $(echo and stuff))
hello world! ... and stuff

Я думаю, что он работает в основном, однако он неправильно отмечает конец расширенной строки, например, если я делаю:

$> echo a $(echo b $(echo c))
a b c
$> echo d $(echo e)
d e c

Смотрите, он печатает c, хотя я этого не просил. Вот мой код:

msh.c — http://pastebin.com/sd6DZYwB expand.c — http://pastebin.com/uLqvFGPw

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

Main находится в msh.c, здесь он получает строку ввода либо из командной строки, либо из файла оболочки, а затем вызывает processline (char *line, int outFD, int waitFlag), где line — это только что полученная строка, outFD — это файловый дескриптор выходного файла, а waitFlag сообщает нам, следует ли нам ждать, если мы разветвимся. Когда мы вызываем это из main, мы делаем это так:

processline (buffer, 1, 1);

В processline мы выделяем новую строку:

char expanded_line[EXPANDEDLEN];

Затем мы вызываем расширение в файле expand.c:

expand(line, expanded_line, EXPANDEDLEN);

В расширении мы буквально копируем символы из строки в Expand_line, пока не найдем $(, который затем вызывает:

static int expCmdOutput(char *orig, char *new, int *oldl_ind, int *newl_ind)

orig — это строка, а new — расширенная строка. oldl_ind и newl_ind — текущие позиции в строке и развернутой строке соответственно. Затем мы передаем и рекурсивно вызываем processline, передавая ему вложенную команду (например, если бы у нас было «echo a $(echo b)», мы бы передали processline «echo b»).

Вот где я запутался, каждый раз, когда вызывается расширение, он выделяет новый кусок памяти EXPANDEDLEN long? Если это так, это плохо, потому что у меня очень быстро закончится место в стеке (в случае сильно вложенного ввода командной строки). В расширении я вставляю нулевой символ в конец расширенной строки, так почему же он печатается за его пределами?

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


person Optimus_Pwn    schedule 19.11.2012    source источник
comment
Я взглянул на код, и он слишком велик, чтобы поощрять тщательное изучение. Вероятно, вы где-то не добавляете нулевой указатель в конец списка аргументов. Вы должны попробовать некоторые альтернативные последовательности, которые повторяют более одного аргумента во внутренних эхо-командах. Я собираюсь порекомендовать некоторые общие методы отладки. Одна из возможностей — просто запустить код под отладчиком. Я склонен использовать методы старой школы «вставить операторы печати». Я бы добавил операторы печати туда, где я могу определить, где добавлен дополнительный аргумент или не установлен указатель NULL. Удачи.   -  person Jonathan Leffler    schedule 19.11.2012
comment
Я думаю, что я указал на это немного больше, так как я опубликовал это. Когда я выполняю exec(echo), я ожидал, что после вывода будет вставлен нулевой символ. Как мне написать один нулевой символ в том месте, где эхо перестало писать? Я думаю, что это решит всю мою проблему здесь. Спасибо.   -  person Optimus_Pwn    schedule 19.11.2012


Ответы (1)


Ваша проблема заключается в expCmdOutput. Как вы уже заметили, вы не получаете строки с завершением NUL при чтении вывода вашего дочернего процесса с использованием read. Что вы хотите сделать, так это завершить строку вручную, добавив что-то вроде

    buf[bytes_read] = '\0';

после вашего вызова read в строке 29 (expand.c). Если вам нужно место для NUL, вы, конечно, можете прочитать только до BUF_SIZE - 1 байт.

Однако вам, вероятно, следует переосмыслить весь цикл, который вы делаете после этого:

    /* READ OUTPUT OF COMMAND FROM READ END OF PIPE, THEN CLOSE READ END */
    bytes_read = read(fd[0],buf,BUF_SIZE);
    while(bytes_read > 0)
    {
            bytes_read = read(fd[0], buf, BUF_SIZE);
            if (bytes_read == -1) perror("read");
    }
    close(fd[0]);

Если вывод вашей команды длиннее BUF_SIZE, вы просто читаете снова до buf, перезаписывая только что прочитанный вывод. Что вам действительно нужно здесь, так это выделить память и добавить в конец, используя strcat (или удерживая указатель на конец вашей строки для эффективности).

person Yefim Dinitz    schedule 19.11.2012