【问题标题】:Why can't pass the variable's value into file in /etc directory?为什么不能将变量的值传递到 /etc 目录中的文件中?
【发布时间】:2019-08-11 22:35:17
【问题描述】:

我想用 bash 将 $ip 变量的值传递到文件 /etc/test.json

ip="xxxx"
sudo bash -c 'cat > /etc/test.json <<EOF
    {
        "server":"$ip",
     }
EOF'

我希望/etc/test.json 的内容是

{
    "server":"xxxx",
 }

然而/etc/test.json中的真实内容是:

{
    "server":"",
 }

但是如果我用/tmp替换目标目录/etc/

ip="xxxx"
cat > /tmp/test.json <<EOF
    {
        "server":"$ip",
    }
EOF

$ip 变量的值被传递到/tmp/test.json

$ cat /tmp/test.json
{
    "server":"xxxx",
 }

Kamil Cuk's 示例中,子进程是cat &gt; /etc/test.json,其中不包含任何变量。

sudo sh -c 'cat > /etc/test.json' << EOF
    {
        "server":"$ip",
    }
EOF

它根本不导出$ip 变量。

现在我们来分析一下:

ip="xxxx"
sudo bash -c "cat > /etc/test.json <<EOF
    {
        "server":\""$ip"\",
     }
EOF"

中的不同部分

"cat > /etc/test.json <<EOF
    {
        "server":\""$ip"\",
     }
EOF"

将连接成一个长字符串并作为命令。为什么$ip变量可以在这里继承其父进程的值?

【问题讨论】:

  • Shell 变量没有用单引号展开。
  • 使用jq 之类的工具来生成 JSON,而不是字符串插值(存在引用错误的风险)。

标签: bash variables


【解决方案1】:

这种行为有两个原因:

  1. 默认情况下,变量不会传递到随后执行的命令的环境中。
  2. 变量未在当前上下文中展开,因为您的命令用单引号括起来。

导出变量

在变量前放置export 语句,参见man 1 bash

提供的名称被标记为自动导出到随后执行的命令的环境中。

正如 Léa Gris 所指出的,您还需要告诉 sudo 使用 -E--preserve-environment 标志来保护环境。

export ip="xxxx"
sudo -E bash -c 'cat > /etc/test.json <<EOF
    {
        "server":"$ip",
     }
EOF'

在当前上下文中展开变量:

这是您的第二个命令起作用的原因,在此示例中,此处文档周围没有任何引号。

但是,如果我将目标目录 /etc/ 替换为 /tmp [...],$ip 变量的值将被传递到 /tmp/test.json


您可以通过将单引号替换为双引号并转义您的ip周围的引号来更改您原来的sn-p:

ip="xxxx"
sudo bash -c "cat > /etc/test.json <<EOF
    {
        "server":\""$ip"\",
     }
EOF"

编辑:关于您的其他问题:

在 Kamil Cuk 的示例中,子进程是 cat > /etc/test.json,其中不包含任何变量。

sudo sh -c 'cat > /etc/test.json' << EOF
    {
        "server":"$ip",
    }
EOF

它根本不导出$ip 变量。

正确,您没有将此处的文档用单引号括起来。因此$ip 在当前上下文中被替换并且传递给子进程标准输入的字符串是

{
    "server":"xxxx",
}

所以在这个例子中子进程不需要知道$ip变量。

简单示例

$ x=1
$ sudo -E sh -c 'echo $x'
[sudo] Password for kalehmann:

这没有任何反应,因为

  • 'echo $x' 用单引号括起来。 $x 因此在当前上下文中不会被替换
  • $x 未导出。因此子流程不知道它的价值。
$ export y=2
$ sudo -E sh -c 'echo $y'
[sudo] Password for kalehmann:
2

这与 2 相呼应,因为

  • 'echo $y' 用单引号括起来。 $x 因此在当前上下文中不会被替换
  • $y 已导出。因此,子流程确实知道它的价值。
$ z=3
$ sudo -E sh -c "echo $z"
[sudo] Password for kalehmann:
3

这与 3 相呼应,因为

  • "echo $z" 用双引号括起来。 $z 因此在当前上下文中被替换

【讨论】:

  • 不需要sudo --preserve-env=ip bash -c 'stuffs_with "$ip"' 将ip 作为环境变量传递给以sudo 运行的bash 吗?
  • 请详细解释我添加的谜题。
  • @it_is_a_literature 我已经更新了我的答案。重要的不是子流程周围的单引号,而是此处文档周围的单引号
  • @LéaGris 是的,你是对的,这在我的第一个示例中是必需的
【解决方案2】:

在子shell 中几乎不需要做here 文档。就在外面做吧。

sudo tee /etc/test.json <<EOF
    {
        "server":"$ip",
    }
EOF

sudo sh -c 'cat > /etc/test.json' << EOF
    {
        "server":"$ip",
    }
EOF

【讨论】:

    【解决方案3】:

    通常,使用字符串插值构建 JSON 片段是不安全的,因为它要求您确保变量被正确编码。让jq 之类的工具为您服务。

    jq 的输出传递给tee,并使用sudo 运行tee 以确保您以root 身份执行的唯一 操作是以正确的权限打开文件.

    ip="xxxx"
    
    jq --arg x "$ip" '{server: $x}' | sudo tee /etc/test.json > /dev/.null
    

    【讨论】:

      猜你喜欢
      • 2010-12-26
      • 1970-01-01
      • 2019-03-04
      • 2011-02-05
      • 1970-01-01
      • 1970-01-01
      • 2015-09-08
      • 2014-11-15
      • 2021-03-21
      相关资源
      最近更新 更多