Переменные окружения в простой оболочке Linux - PullRequest
2 голосов
/ 30 ноября 2011

Я должен программировать простую оболочку на C для моего проекта, которая может реализовывать переменные среды. Я посмотрел, как использовать getenv, setenv, putenv. Пока все хорошо, я пытался использовать getenv для отображения переменных оболочки ... ну ... с некоторыми успехами. Но у меня есть ощущение, что мои рассуждения ошибочны. У меня есть char** argv, который содержит проанализированный ввод от ввода пользователя. Теперь я проверяю, начинается ли argv с команды «эхо», а затем, если любой из следующих входов начинается со знака $ или нет. Вот мой код:

int executeVariables(char** arguments){
    int i = 0;
    if(strcmp(arguments[i], "echo") == 0){
        char *variable;
        for(i = 1; arguments[i] != NULL; i++){
            char *str = arguments[i];
            if( *(str + 0) == '$'){
                variable = getenv(str + 1);
            }else{
                variable = getenv(str);
            }
            if(!variable){
                //puts("not a variable");
                printf("%s ", arguments[i]);
            }else{
                //puts("a variable");
                printf("%s ", variable);
            }
        }
        printf("\n");
        exit(0);
    }

    return 1;

}

Я думаю, что нормальная оболочка linux находит знак $, она расширяет переменную перед вызовом команды echo. Моя оболочка не следует этому принципу, она расширяет переменные внутри самой команды echo. Любая идея о том, как я могу это реализовать? Благодарю.

EDIT:

Проблема, с которой я столкнулся: echo $HOME и echo HOME дает мне тот же результат, что и неверный.

EDIT:

После различных тестов все работает хорошо. Но чтобы действительно это проверить, мне нужно создать локальную переменную, а затем echo это значение. Я пробовал использовать putenv функцию, но она не создает локальную переменную.

i = 0;
char** temp = malloc(sizeof (*temp));
if(strstr(userInput, "=") != NULL){
    //puts("we got equals");
    puts(userInput);
    if(putenv(userInput) == 0){
       printf("doing putenv(%s)\n", userInput);
        exit(0);
    }
    else{
        puts("couldnt putenv");
       exit(1);
    }
}

userInput: char *userInput - это ввод, полученный из командной строки с использованием fgets()

Ответы [ 2 ]

4 голосов
/ 30 ноября 2011

Вы специально запрашиваете код для выполнения getenv () для строки, даже если $ не найден. Вот почему он будет искать $ HOME или HOME. Просто удалите случай else, чтобы не найти знак доллара, и обязательно инициализируйте переменную NULL при ее объявлении и поместите его в цикл.

Примерно так:

// First, perform replacements
int executeVariables(char** arguments){
    int i = 0;

    for(i = 0; arguments[i] != NULL; i++){
        char *str = arguments[i];

        if(*str == '$'){
            // make sure the result isn't NULL, though I'm not sure a real shell does
            char *tmp = getenv(str + 1);
            if (tmp != NULL) {
                arguments[i] = getenv(str + 1); // save off the argument
            }
        }
    }

    // Then actually execute the function. This would be like bash's echo builtin
    if (strcmp(arguments[0], "echo") == 0) {
        int i;
        for (i = 1; arguments[i] != NULL; i++) {
            printf("%s ", arguments[i]);
        }
        printf("\n");
    }

    // Other functions could go here

    return 1;
}

Редактировать: Что касается вашей методологии, почему вы специально проверяете эхо? Почему бы не сделать его общим и проверить все аргументы, включая первый? На самом деле вы, вероятно, захотите заменить все потенциальные переменные среды, поэтому, если у вас где-нибудь есть MYECHO = echo, будет работать следующее. Чтобы сделать его более общим, у вас будет эта функция, которая затем будет выполнять материал на основе расширенных переменных. Вы могли бы сделать все это красиво и иметь отдельные функции, но чтобы вместить все это здесь, я соответственно обновил приведенный выше код, хотя я не проверял его. ;)

Редактировать: При этом замечание оборотня о том, как сделать это ранее, действительно применимо - замените аргументы, как здесь, и затем используйте этот обновленный массив аргументов, чтобы отдельная функция делала все, что ей нужно , :)

> $MYVAR totally awesome $HOME
totally awesome /home/user

Редактировать: Что касается ситуации с putenv (), вам понадобится нечто структурированное, как показано ниже. Таким образом, он установит его для оболочки и любых других процессов, которые вы запускаете в оболочке.

void do_args_env(char *args[])
{
    // do putenv, etc.
}

// inside main loop in shell process
while (1) { // just an example
    if (check_args_syntax(args) != 0) {
        // error
    }

    do_args_env(args);

    // fork and do other stuff
}

(Надеюсь, окончательный вариант) Редактировать: В качестве объяснения процессы обычно не (возможно, не могут?) Влиять на среду процессов над ними в их иерархии; только наоборот. Поэтому, если вы выполняете putenv () в дочернем элементе, родные элементы этого дочернего элемента (то есть другие процессы, разветвленные от его родительского элемента) не получат изменения среды.

Рад, что могу помочь!

1 голос
/ 30 ноября 2011

Да, обычные оболочки расширяют переменные во время синтаксического анализа командной строки.Таким образом, вам нужно заменить переменные в функции, которая создает массив, который «содержит анализируемый ввод от пользовательского ввода».В этом случае код для echo (и других команд) будет намного короче.

...