如何在 Terraform local-exec provisioner 中编写兼容 Windows 的命令行
How to write Windows-compatible command lines in Terraform local-exec provisioner
我正在尝试 运行 使用 terraform 的 local-exec
provisioner 在 windows 下执行命令(curl
调用)。根据日志,它 运行 使用 cmd /C
。
粘贴在终端中的相同命令工作正常。
resource "null_resource" "search_indexes" {
for_each = local.indexes_set
provisioner "local-exec" {
command = <<EOT
curl https://${var.cognitive_search_name}.search.windows.net/indexes?api-version=2021-04-30-Preview --header 'Accept: application/json' --header 'api-key: ${var.key}' --data '@${each.key}'
EOT
}
}
输出结果如下(节选进度报告):
module.(...).search_indexes["test-index.json"] (local-exec): Executing: ["cmd" "/C" "curl ...
exit status 6.
curl: (6) Could not resolve host: application // actually part of first header argument
curl: (6) Could not resolve host: 1...F' // actually part of second header argument, api-key
所以我看到它是 运行 和 cmd /C
并且 --header 参数中 space 之后的单词被认为是新的 URL。
在参数周围使用双引号,例如--headers
,有些转义,但仍然报同样的错误。
在 windows 终端中复制粘贴的命令工作正常。
我怀疑 cmd
或 terraform 做了一些改变命令行的事情,所以它看起来不正确。我怎样才能实现正确的行为?
Unix 和 Windows 在命令行上引用和转义的过程和约定有很大不同。
在 Unix 上,引用和转义是 shell 本身的一个问题,Unix shells 通常将单引号 '
的内容理解为完全逐字的文本,而"
的内容可以包含一些插值元字符,但空格仍然按字面意思使用,而不是分隔符。 shell 将标记化处理为一组参数,并将这些参数作为字符串数组传递给程序。
在Windows上,引用和转义是程序关注的问题运行1,不是 命令解释器 cmd.exe
。您键入的命令行将(或多或少)完全按照您将其写入您正在 运行ning 的程序的方式传递——在本例中为 curl
——以及 it's left entirely up to that program to decide 什么符号像 "
和 '
可能意味着以及在何处划定不同参数之间的界限。
实际上,Windows 上的大多数现代 CLI 应用程序要么使用 Microsoft Visual C++ 构建,要么使用其他一些模拟其命令行解析约定的软件构建。如果 curl
是这样的程序(这很可能,因为它是用 C 编写的)那么不幸的是 the relevant rules are quite complicated,但为了您的目的,我们可以将其简化为几个关键事实:
- 单引号字符
'
按字面意思取。与 Unix 不同,它不表示逐字文本。
- 您可以使用双引号字符
"
来表示一个序列,其中空格应按字面意思使用而不是用作参数分隔符。
因此,为了编写可跨 Unix 和 Windows 移植的命令行,您将需要使用双引号字符 "
并确保您在引号内插入的字符串不包含任何会被任何一组解析规则解释为特殊的字符。
在你的情况下,我认为只要你能确定 none 所引用的变量将包含引号字符、$
字符或其他任何内容,就会是以下内容Unix shell 可能解释为双引号内的特殊内容:
command = <<-EOT
curl "https://${var.cognitive_search_name}.search.windows.net/indexes?api-version=2021-04-30-Preview" --header "Accept: application/json" --header "api-key: ${var.key}" --data "@${each.key}"
EOT
由于 Unix 和 Windows 之间的显着架构差异,编写可移植命令行非常具有挑战性,这也是 the Terraform documentation recommends considering provisioners as a last resort 的原因之一。如果可能的话,通常最好使用专门的提供商来提出这样的请求,但我对您在此处请求的 API 不够熟悉,无法知道哪个提供商可能会提供它。
1 从技术上讲,命令解释器 会 自己做一些解析来处理环境变量替换,例如 %%FOO%%
和I/O 重定向序列,如管道和 >
/<
标记。
命令解释器因此具有 its own separate escaping syntax 如果您的命令行最终包含任何 命令解释器的 特殊字符,它可能会发挥作用。
幸运的是,命令解释器的解析器也对"
有一些基本的理解,作为关闭某些它的标记特殊处理,但我们仍然需要注意引号内的 %%VARIABLE%%
序列,因此您需要确保要替换的变量不包含类似的内容。
我正在尝试 运行 使用 terraform 的 local-exec
provisioner 在 windows 下执行命令(curl
调用)。根据日志,它 运行 使用 cmd /C
。
粘贴在终端中的相同命令工作正常。
resource "null_resource" "search_indexes" {
for_each = local.indexes_set
provisioner "local-exec" {
command = <<EOT
curl https://${var.cognitive_search_name}.search.windows.net/indexes?api-version=2021-04-30-Preview --header 'Accept: application/json' --header 'api-key: ${var.key}' --data '@${each.key}'
EOT
}
}
输出结果如下(节选进度报告):
module.(...).search_indexes["test-index.json"] (local-exec): Executing: ["cmd" "/C" "curl ...
exit status 6.
curl: (6) Could not resolve host: application // actually part of first header argument
curl: (6) Could not resolve host: 1...F' // actually part of second header argument, api-key
所以我看到它是 运行 和 cmd /C
并且 --header 参数中 space 之后的单词被认为是新的 URL。
在参数周围使用双引号,例如--headers
,有些转义,但仍然报同样的错误。
在 windows 终端中复制粘贴的命令工作正常。
我怀疑 cmd
或 terraform 做了一些改变命令行的事情,所以它看起来不正确。我怎样才能实现正确的行为?
Unix 和 Windows 在命令行上引用和转义的过程和约定有很大不同。
在 Unix 上,引用和转义是 shell 本身的一个问题,Unix shells 通常将单引号 '
的内容理解为完全逐字的文本,而"
的内容可以包含一些插值元字符,但空格仍然按字面意思使用,而不是分隔符。 shell 将标记化处理为一组参数,并将这些参数作为字符串数组传递给程序。
在Windows上,引用和转义是程序关注的问题运行1,不是 命令解释器 cmd.exe
。您键入的命令行将(或多或少)完全按照您将其写入您正在 运行ning 的程序的方式传递——在本例中为 curl
——以及 it's left entirely up to that program to decide 什么符号像 "
和 '
可能意味着以及在何处划定不同参数之间的界限。
实际上,Windows 上的大多数现代 CLI 应用程序要么使用 Microsoft Visual C++ 构建,要么使用其他一些模拟其命令行解析约定的软件构建。如果 curl
是这样的程序(这很可能,因为它是用 C 编写的)那么不幸的是 the relevant rules are quite complicated,但为了您的目的,我们可以将其简化为几个关键事实:
- 单引号字符
'
按字面意思取。与 Unix 不同,它不表示逐字文本。 - 您可以使用双引号字符
"
来表示一个序列,其中空格应按字面意思使用而不是用作参数分隔符。
因此,为了编写可跨 Unix 和 Windows 移植的命令行,您将需要使用双引号字符 "
并确保您在引号内插入的字符串不包含任何会被任何一组解析规则解释为特殊的字符。
在你的情况下,我认为只要你能确定 none 所引用的变量将包含引号字符、$
字符或其他任何内容,就会是以下内容Unix shell 可能解释为双引号内的特殊内容:
command = <<-EOT
curl "https://${var.cognitive_search_name}.search.windows.net/indexes?api-version=2021-04-30-Preview" --header "Accept: application/json" --header "api-key: ${var.key}" --data "@${each.key}"
EOT
由于 Unix 和 Windows 之间的显着架构差异,编写可移植命令行非常具有挑战性,这也是 the Terraform documentation recommends considering provisioners as a last resort 的原因之一。如果可能的话,通常最好使用专门的提供商来提出这样的请求,但我对您在此处请求的 API 不够熟悉,无法知道哪个提供商可能会提供它。
1 从技术上讲,命令解释器 会 自己做一些解析来处理环境变量替换,例如 %%FOO%%
和I/O 重定向序列,如管道和 >
/<
标记。
命令解释器因此具有 its own separate escaping syntax 如果您的命令行最终包含任何 命令解释器的 特殊字符,它可能会发挥作用。
幸运的是,命令解释器的解析器也对"
有一些基本的理解,作为关闭某些它的标记特殊处理,但我们仍然需要注意引号内的 %%VARIABLE%%
序列,因此您需要确保要替换的变量不包含类似的内容。