地形 |替换字符串中的多个子字符串

Terraform | Replacing multiple substrings in a string

我有一个字符串,其中有多个子字符串需要替换。

字符串: string1 = "I want to fly tomorrow"

我需要从地图上取走任何需要更换的东西。

地图:

map1 = {
    "I": "we"
    "want": "do not want"
    "tomorrow": "today"
  }

因此 map1 映射中的键是 string1 字符串中需要替换的键,值应该是字符串中的新值。

结果应该是这样的: we do not want to fly today

我一直在想一个解决方案,但我什至没有接近。

我试过这个:

try = [for replacement in keys(local.map1): replace(local.string1, replacement, local.map1[replacement])]

但是这个 returns 一个字符串列表,每个字符串只替换了一个值。

注意string1map1 只是示例,它们可以有任何其他值,所以我我正在寻找一个通用的解决方案:)

我觉得你这样做是这样的:

variable "string1" {
  default = "I want to fly tomorrow"
}

variable "map1" {
  default = {
    "I" = "we"
    "want" = "do not want"
    "tomorrow" = "today"
  }
}

output "test" {
  value = join(" ", [for word in split(" ", var.string1): lookup(var.map1, word, word)]) 
}

输出为:

we do not want to fly today

这是工作需要的东西;这是在 Terraform 0.12.23 上测试的。与 Marcin 的回答不同,因为这适用于彼此相邻、字符之间(例如引号)或不被空格分隔的占位符。

鉴于我们有一个包含 {} 格式占位符的文件,我需要将它们替换为 key/value 对的映射。

这是您可以做到的 copy/paste。

sample.txt

Hello {name}, my favorite food is {food} I'd
  like to eat {food} and sleep.
  Here's a {non_existent} variable

playground.tf

locals {
  input = file("./sample.txt")

  map = {
    food = "Sushi"
    name = "Data_sniffer"
  }

  out = join("\n", [
    for line in split("\n", local.input) :
      format(
        replace(line, "/{(${join("|", keys(local.map))})}/", "%s"),
        [
          for value in flatten(regexall("{(${join("|", keys(local.map))})}", line)) :
            lookup(local.map, value)
        ]...
      )
  ])
}

output "test_out" {
  value = local.out
}

输出

$ terraform apply

Apply complete! Resources: 0 added, 0 changed, 0 destroyed.

Outputs:

test_out = Hello Data_sniffer, my favorite food is Sushi I'd
  like to eat Sushi and sleep.
  Here's a {non_existent} variable

这里解释一下这个魔法是如何工作的:

  1. 第一步是用换行符分割你的文本,这样你就可以使用以下内容:

    [
      "Hello {name}, my favorite food is {food} I'd",
      "  like to eat {food} and sleep.",
      "  Here's a {non_existent} variable"
    ]
    
  2. 现在我们要替换我们的占位符。但是,在撰写本文时,Terraform 没有一种简单的方法可以轻松地在一个字符串中替换多个字符串。所以我们同时使用 format 函数;它的占位符需要 %s 。基本上,我们希望我们的数组现在看起来像这样:

    [
      "Hello %s, my favorite food is %s I'd",
      "  like to eat %s and sleep.",
      "  Here's a {non_existent} variable"
    ]
    

    为了对每一行进行这种转换,我们想用 %s 替换有效的 {} 占位符;即我们不想替换地图中不存在的 {non_existent}

    我们利用 Terraform 允许您进行正则表达式替换这一事实,因此我们需要以下正则表达式:{(food|name)}(其中 foodname 是我们想换)。由于我们不想手动维护此列表,因此我们使用 keys() and string interpolation.

    以编程方式构建正则表达式
    "/{(${join("|", keys(local.map))})}/"
    

    现在我们有了正则表达式,我们可以使用 replace,它支持正则表达式。

    replace(line, "/{(${join("|", keys(local.map))})}/", "%s")
    
  3. 但是等等!我们现在只有 %s,我们怎么知道要按什么顺序替换什么???这就是 regexall() 的用武之地( 而不是 regex())。使用上面 almost 相同的正则表达式,我们将再次提取匹配的占位符。

    (上面的正则表达式和这个的唯一区别是开头和结尾缺少 /

    regexall("{(${join("|", keys(local.map))})}", "Hello {name}, my favorite food is {food} I'd")
    

    此函数调用将 return 我们找到的所有占位符。在这里使用 regexall() 很重要,否则它会在第一场比赛中停止。

    [
      [
        "name"
      ],
      [
        "food"
      ]
    ]
    

    我们这里不需要双重嵌套列表,所以我们将使用 flatten()

    flatten(
      regexall("{(${join("|", keys(local.map))})}", "Hello {name}, my favorite food is {food} I'd")
    )
    

    现在 return,

    [
      "name",
      "food",
    ]
    

    现在,我们可以使用这个 列表,通过lookup() 函数从地图中查找相应的值。

    [
      for value in flatten(regexall("{(${join("|", keys(local.map))})}", "Hello {name}, my favorite food is {food} I'd")) :
        lookup(local.map, value)
    ]
    

    哪个 returns:

    [
      "Data_sniffer",
      "Sushi",
    ]
    

    然后可以将此替换值数组输入到我们的顶级 format() 调用中。但是,format() 不接受它期望以逗号分隔的参数的数组。所以使用 ... operator 将列表扩展为函数参数。