【问题标题】:Environment variables in simple linux shell简单 linux shell 中的环境变量
【发布时间】:2012-01-09 20:04:22
【问题描述】:

我要为我的项目用 C 语言编写一个简单的 shell,它可以实现环境变量。我查看了如何使用 getenv、setenv、putenv。到目前为止一切顺利,我尝试使用 getenv 来显示 shell 变量……嗯……取得了一些成功。但我有一种感觉,我的推理是有缺陷的。我有一个char** argv,其中包含来自用户输入的解析输入。我现在检查argv 是否以命令“echo”开头,然后检查以下任何输入是否以$ 符号开头。这是我的代码:

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 shell 会找到 $ 符号,它会在调用 echo 命令之前扩展变量。我的 shell 没有遵循这个原则,它在 echo 命令本身内扩展变量。关于如何实现这一点的任何想法?谢谢。

编辑:

我遇到的一个问题是:echo $HOMEecho HOME 给了我相同的结果,这是错误的。

编辑:

经过各种测试,一切正常。但要真正测试它,我需要创建一个局部变量然后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()从命令行获取的输入

【问题讨论】:

  • 您有什么问题?看起来你的代码会达到你想要的效果,不是吗?
  • @DanFego 不完全是:echo $HOMEecho HOME 给了我相同的结果,这是错误的。
  • putenv() 失败,还是返回 0?
  • 为了记录,整个问题可能应该被分开,但既然我们还在这里,我会编辑我的答案。 :P
  • 哦,等等,putenv() 不应该接受 userInput,而不是 *userInput?

标签: c linux shell environment-variables getenv


【解决方案1】:

您特别要求代码为字符串执行 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,则以下内容将起作用。为了使其更通用,您将拥有此函数,然后它将根据扩展变量执行内容。你可以把它做得很好并且有单独的功能,但是为了把它都放在这里,我已经相应地更新了上面的代码,虽然我还没有测试过。 ;)

编辑:话虽如此,werewindle 之前关于这样做的评论确实适用 - 替换参数,就像这里一样,然后使用更新的参数数组让一个单独的函数做它需要做的任何事情做。 :)

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

编辑: 至于 putenv() 情况,您需要如下结构。这样,它将为 shell 以及您在 shell 中运行的任何其他进程设置它。

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
}

(希望是最终版)编辑: 作为解释,流程通常不会(也许不能?)影响其层次结构中高于它们的流程的环境;反之亦然。因此,如果您将env() 放入一个孩子中,该孩子的兄弟姐妹(即从其父级派生的其他进程)将不会改变环境。

很高兴我能帮上忙!

【讨论】:

  • 感谢您的帮助。它解决了这个问题。我不知道为什么我什至保留了else 条件。不管怎样,你觉得我的推理是对的吗?我的意思是我没有遵循正常的 shell 行为。
  • 精确。那是我的问题。我该怎么做呢?我想让它通用。将删除第一个if 条件,然后搜索每个参数包含一个美元符号,然后用正确的值替换argv,然后使用execvp(*argv, argv) 吗?
  • 更新了代码,但 werewindle 下面的评论为您指明了正确的方向。有你的参数数组,执行替换作为它自己的操作,然后做剩下的。
  • 谢谢。让我试试。我会用新代码更新我的问题。
  • 经过各种测试,一切正常。但要真正测试它,我需要创建一个局部变量。我尝试使用putenv 函数,但它没有创建局部变量。请检查我编辑的问题。
【解决方案2】:

是的,普通 shell 在解析命令行时会扩展变量。所以你需要函数中的替代变量,它产生“包含来自用户输入的解析输入”的数组。在这种情况下,echo(和其他命令)的代码会更短。

【讨论】:

  • 对不起,我没有完全理解你的第二句话。
  • 当您解析用户输入时(例如“在 $HOME 回声我的家”),您必须用实际变量值替换 $HOME。因此,在调用executeVariables 期间,arguments 中将是要处理的实际数据(即“echo”、“my”、“home”、“at”、“/root”)。所有其他 shell 都以这种方式工作。
  • 对不起,我想,你问如何更好地替换环境变量。要修复您的代码,只需将 variable = getenv(str); 替换为 variable = NULL
  • 好的,谢谢。解决了。如果我想更好地替换环境变量? :)
  • 那么你应该早点替换它们:)。或者您很快就会发现,在处理任何 shell 命令期间,您具有替换环境变量的相同代码。另外,不要忘记,变量还有其他语法,例如 ${MYVAR}。我想创建一个特殊的函数subs_env(char *str),它将在解析用户输入的早期步骤中用它们在str 中的值替换所有可能的变量。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-03-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-28
  • 1970-01-01
相关资源
最近更新 更多