【问题标题】:The eval command does not work inside a loop [duplicate]eval 命令在循环内不起作用 [重复]
【发布时间】:2017-06-24 07:19:39
【问题描述】:

如果我有一个简单的 bash 脚本 set_token.sh:

#!/bin/bash

output='export AWS_ACCESS_KEY_ID="111"
export AWS_SECRET_ACCESS_KEY="222"
export AWS_SESSION_TOKEN="333"'

echo "$output" | while read line; do eval $line; done

执行set_token.sh 没有成功设置3个环境变量。但是,如果我分别在每一行上运行 eval,它就可以工作。

$ eval 'export AWS_ACCESS_KEY_ID="111"'
$ eval 'export AWS_SECRET_ACCESS_KEY="222"'
$ eval 'export AWS_SESSION_TOKEN="333"'

为什么会这样?

【问题讨论】:

  • 您在管道时在子shell的环境中设置变量;见BashFAQ 24
  • @Tony Vu:您打算让我们明确您的要求吗?你的问题解决了还是卡住了?
  • 修复BashFAQ #24后,执行脚本将设置三个变量...并退出,新分配的变量会随着它们设置的shell的死亡而消失。export设置变量会修改 进程的环境,而不是父进程。

标签: bash shell eval


【解决方案1】:

不确定在这种情况下为什么要使用 eval。为什么不像这样更直接地设置变量:

export AWS_ACCESS_KEY_ID="111"
export AWS_SECRET_ACCESS_KEY="222"
export AWS_SESSION_TOKEN="333"

您的循环在子 shell 中运行(因为echo "$output" | ...),这就是为什么您的变量在外部可见。不是eval 不工作!别担心 - 很多人都会遇到这种情况。

如果你坚持使用循环和eval,你可以使用进程替换< <(command)

while read line; do eval $line; done < <(printf "%s\n" "$output")

【讨论】:

  • ++ 对于正确的方法,顺便说一句,将您的答案嵌入我的,强调不要使用 eval 并推荐其他方法。
  • 谢谢。就我而言,变量是动态生成的。为了简单起见,我只是在这里使用了输出。这似乎是正确的原因,但进程替换仍然对我不起作用。
  • @TonyVu:那你还要找什么?
  • @TonyVu:你能举一个variables are generated dynamically的案例吗?提供一个最小的例子来测试
  • @Inian:我的意思是用@codeforester 的建议while read -r line; do eval $line; done &lt; &lt;(printf "%s\n" "$output") 替换脚本中的最后一行,但环境变量仍然没有设置
【解决方案2】:

您可以在没有循环和没有eval 的情况下获得所需的结果。

source <(echo "$output")

&lt;() 构造是一个进程替换。它执行里面找到的命令,创建一个 FIFO(特殊的先进先出文件),然后转换为source 可以读取的实际文件路径(指向 FIFO)。

当然,您也可以将实际分配存储在一个文件中,而不是将它们放在output 变量中。

source config_file

source 命令(或其更标准的形式.)从文件中读取命令并在当前 shell 中执行它们,而无需启动单独的进程或子 shell,因此源文件中的变量分配有效。对配置文件很有用,但当然你必须确保没有人可以在这些文件中放置任意命令,因为这会带来安全风险。

重要

如果您想将声明放入脚本中(在您的情况下为set_token.sh),则此脚本必须 来源(即使用source. 执行),而不是使用@ 执行987654332@ 或直接调用它(如果它是可执行的)。除了source. 之外的任何方法都将启动子进程,并且子进程无法分配之后对父进程可见的变量。采购不会创建单独的流程,这就是分配工作的原因。 export 关键字将使分配仅对子进程可见,它们不能使分配对父进程可见。

【讨论】:

  • 谢谢,但是没有用
  • @TonyVu 请参阅我添加到答案中的重要部分,我强烈怀疑它解释了为什么它对您不起作用(因为该技术本身有效,毫无疑问)。跨度>
  • 嗨@Fred,使用源或.工作。
  • 好答案,但要明确一点:如果输入来自 file 无论如何,source (.) 是正确的命令;如果没有,只需在字符串上直接使用eval(也适用于多行); source 带有进程替换在这种情况下没有任何优势 - 相反:它涉及一个子外壳。 sourceeval 具有同样的风险:它们盲目地评估传递给它们的任何命令(在当前 shell 的上下文中)。
【解决方案3】:

Fred's helpful answer 包含一个可行的解决方案和良好的指示(Bash FAQ #24 - "I set variables in a loop that's in a pipeline. Why do they disappear after the loop terminates?" 解释了原始方法的问题)。

也就是说,在您的特定场景中 - 假设您愿意接受使用 eval 的风险 - 您可以将其直接应用于您的多行字符串:

#!/bin/bash

output='export AWS_ACCESS_KEY_ID="111"
export AWS_SECRET_ACCESS_KEY="222"
export AWS_SESSION_TOKEN="333"'

# This defines all 3 AWS_* environment variables.
eval "$output"

重申 Fred 的观点:要使环境变量在 current shell 中生效,您必须 source 脚本(使用内置 . 或其(有效)别名source):

. ./set_token.sh

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-28
    • 2021-12-31
    • 2016-02-06
    • 1970-01-01
    相关资源
    最近更新 更多