使用 AWK 打印目录搜索和过滤的两个不同结果

Print two different results from a directory search and filter using AWK

我正在尝试使用 AWK 匹配并打印来自目录搜索和过滤的两个结果。我的目录包含许多 Terraform 文件,我正在尝试查找每个文件的提供程序和版本。

这里有两个完全不同的例子,对于其他例子来说可能完全一样,也可能不完全一样;

provider "azurerm" {
  version         = "=1.44.0"
  client_id       = var.ARM_CLIENT_ID
  client_secret   = var.ARM_CLIENT_SECRET
  subscription_id = var.ARM_SUBSCRIPTION_ID
  tenant_id       = var.ARM_TENANT_ID
}

provider "azurerm" {
  features {}
  version         = "<= 2.33"
  client_id       = var.ARM_CLIENT_ID
  client_secret   = var.ARM_CLIENT_SECRET
  subscription_id = var.ARM_SUBSCRIPTION_ID
  tenant_id       = var.ARM_TENANT_ID

  skip_provider_registration  = true
  skip_credentials_validation = true
}

我可以使用 find 命令并使用 'between' 搜索打印出包含该命令的所有文件; find . -name "*.tf" -exec awk '/^provider/,/^}/' {} +

该输出的示例(很多页长),如下所示;

provider "azurerm" {
    version         = "<= 1.40"
    client_id       = var.ARM_CLIENT_ID
    client_secret   = var.ARM_CLIENT_SECRET
    subscription_id = var.ARM_SUBSCRIPTION_ID
    tenant_id       = var.ARM_TENANT_ID
}
provider "azurerm" {
    version           = "=2.34.0"
    features {}
    client_id         = var.ARM_CLIENT_ID
    client_secret     = var.ARM_CLIENT_SECRET
    subscription_id   = var.ARM_SUBSCRIPTION_ID
    tenant_id         = var.ARM_TENANT_ID
}
provider "azurerm" {
    features {}
    version         = "2.2"
    alias           = "prd"
    client_id       = var.ARM_CLIENT_ID
    client_secret   = var.ARM_CLIENT_SECRET
    subscription_id = var.ARM_SUBSCRIPTION_ID
    tenant_id       = var.ARM_TENANT_ID
}

使用下面的命令我可以打印出每个版本; find . -name "*.tf" -exec awk '/^provider/,/^}/' {} + | awk ' ~ /^version[[:space:]]*/ { for (i=2; i<NF; i++) gsub("\"",""); print $NF }'

结果是这样的;

2.2
1.44
=1.42
=1.42
1.41
=1.38.0
=1.42
>2.0.0

使用以下命令,我可以打印出每个供应商的名称; find . -name "*.tf" -exec awk '/^provider/,/^}/' {} + | awk ' ~ /^provider/ { gsub("\"",""); print }'

结果是这样的;

azurerm
panos
azurerm
azurerm
external
azurerm
azurerm
panos

现在我在尝试找出如何将它们组合在一起以使它们看起来像这样时遇到了麻烦; azurerm = 2.2, 甚至

azurerm
2.2

基本上任何可以使它们靠得更近的东西,这样我就可以打印出 providers/versions。目录名称打印也会是一个巨大的好处。

任何有关组合这两个 AWK 命令的帮助都将不胜感激,因为我一直在尝试弄清楚如何使用正则表达式搜索在同一输出中打印两个不同的 key/values。 (&&, ||, ...)`

编辑 只是为了补充 Markp 在下面给出的答案,我设法将它包裹在一个 for 块周围,以便它可以打印出它们所在的文件夹名称;

for i in $( find . -name "*.tf" -execdir sh -c 'pwd' sh {} + | uniq); do
    cd "$i" || exit
    printf '\n\%s\n' "${PWD##*/} contains:"
    find . -maxdepth 1 -name "*.tf" -exec awk '/^provider/,/^}/' {} + | awk -F'"' '/^provider/ { pr= ; next } /version/ { printf "%s %s\n", pr,  }'
done

...只是觉得其他人可能会觉得这很方便

示例输入(由初始 find/awk 调用生成):

$ cat provider.dat
provider "azurerm" {
    version         = "<= 1.40"
    client_id       = var.ARM_CLIENT_ID
    client_secret   = var.ARM_CLIENT_SECRET
    subscription_id = var.ARM_SUBSCRIPTION_ID
    tenant_id       = var.ARM_TENANT_ID
}
provider "azurerm" {
    version           = "=2.34.0"
    features {}
    client_id         = var.ARM_CLIENT_ID
    client_secret     = var.ARM_CLIENT_SECRET
    subscription_id   = var.ARM_SUBSCRIPTION_ID
    tenant_id         = var.ARM_TENANT_ID
}
provider "azurerm" {
    features {}
    version         = "2.2"
    alias           = "prd"
    client_id       = var.ARM_CLIENT_ID
    client_secret   = var.ARM_CLIENT_SECRET
    subscription_id = var.ARM_SUBSCRIPTION_ID
    tenant_id       = var.ARM_TENANT_ID
}

单个 awk 调用来解析和打印 <provider> \n <version>:

awk -F'"' '                                   # use double quotes as input field separator
/^provider/ { pr= ; next }                  # if lines starts with "provider" then save field 2 for later use
/version/   { printf "%s\n%s\n", pr,  }     # if line includes string "version" then print out the provider (pr) and version (field 2 from this line)
' provider.dat

以上生成:

azurerm
<= 1.40
azurerm
=2.34.0
azurerm
2.2

注意:可能需要返回并根据 OP 寻找的最终输出格式调整代码。


假设 providerversion 字符串只出现在 OP 想要的数据块中,我想知道建议的答案是否(上面)可以换出 OP 当前的 awk(由 find 提供)。

无论如何,我认为有可能将所有需要的代码折叠成单个 awk 调用(由 find 提供),这可能需要一些额外的细节 re: sample输入...

sed 替代方案:

sed -En 's/(^provider ")(.*)(".*$)//p;s/(^.*version.*")(.*)("$)//p' provider.dat

根据正则表达式将提供者行分成三部分,并仅用该行替换第二部分和打印。将带有版本的行分成两部分,替换第二部分的行并打印。