james
james

Reputation: 152

awk/sed - to print nth value after the match

My tf variables file has 200+ variables. Here are some conflicting variables when I try awk:

variable "vm_ami" {
  type        = string
  description = "ami image id used for setting up the vm"
  default     = "ami-5157cd3f"
}
variable "vm_mount_name" {
  type        = string
  description = "mount point for new device"
  default     = "xvdb"
  #default     = "xvdb"
}
variable "vm_device_name" {
  type        = string
  description = "device name for new drive"
  default     = "xvdb"
  #default     = "nvme1n1"
}
variable "elb_account_id" {
  type        = string
  description = "account id for elb"
  default     = "01234567890"
}
variable "account_id" {
  type        = string
  description = "Account number"
  default     = "0987654321"
}

I want to print 0987654321 (account ID). For that, I tried:

awk 'c&&!--c;/account_id/{c=3}' variables.tf

Gives:

  default     = "01234567890"
  default     = "0987654321"

.

awk 'c&&!--c;/account_id/{c=3}' variables.tf | cut -d '=' -f 2

Gives:

 "01234567890"
 "0987654321"

how can i print just : 0987654321 (without double quotes)

Upvotes: 0

Views: 142

Answers (4)

M. Nejat Aydin
M. Nejat Aydin

Reputation: 10123

With sed:

sed -n '
    /variable "account_id"/,/^[[:blank:]]*}[[:blank:]]*$/ {
        /^[[:blank:]]*default[[:blank:]]*=[[:blank:]]*"\(.*\)".*/ {
            s//\1/p; q;
        }
    }' file

Explanation:

Assumptions: In the input, there is exactly one line matching the string variable "account_id", and there is exactly one line matching the string default = "..." between the first matching line and the line consisting of }.

The address range /variable "account_id"/,/^[[:blank:]]*}[[:blank:]]*$/ matches the lines starting from the line containing the string variable "account_id", and continues until the line consisting solely of a single }, optionally with leading and/or trailing blank characters (spaces or tabs). You can simplify the second address as /^}$/ if you're sure that there will be no blanks in this line.

The regular expression [[:blank:]]* matches zero or more consecutive blank characters. The regex ^[[:blank:]]*default[[:blank:]]*=[[:blank:]]*"\(.*\)".* matches the entire line which has the form default="...", possibly with leading or intervening blank characters, and captures the string between double quotes ("\(.*\)").

The substitution command s//\1/p replaces the entire line with the string captured previously (between double quotes), then prints it out. The empty regular expression // repeats the last regular expression match.

The q command exits the sed without processing any more commands or input.

Upvotes: 1

Ed Morton
Ed Morton

Reputation: 203189

With GNU awk for FPAT to easily identify fields and then creating an array (f[] below) to map the field names to their values, you can just reference the values you want by their names:

$ awk -v FPAT='[[:alnum:]_]+|"[^"]*"' '
    { gsub(/^"|"$/,"",$2); f[$1]=$2 }
    /^}/ && (f["variable"] == "account_id") { print f["default"] }
' file
0987654321

With that approach it's trivial to test whatever you like and print whatever you like in whatever order you like, e.g.:

$ awk -v FPAT='[[:alnum:]_]+|"[^"]*"' -v OFS=',' '
    { gsub(/^"|"$/,"",$2); f[$1]=$2 }
    /^}/ && ( (f["variable"] == "account_id") || (f["description"] ~ /new/) ) {
        print f["description"], f["default"]
    }
' file
mount point for new device,xvdb
device name for new drive,nvme1n1
Account number,0987654321

Upvotes: 1

Shawn
Shawn

Reputation: 52334

This is a bit heavy weight and more of a thought experiment, but that file is basically tcl syntax, which means it's easy to use tcl to create a simple DSL to parse the file and then extract the desired information.

#!/usr/bin/env tclsh

# Create a new sandboxed interpreter and create functions to store
# variables and their sub-fields in an array.  Treat the sub-fields as
# commands of their own to be evaluated instead of just doing a
# straight list search for default to avoid things like
# # default = "bad" matching.
set i [interp create -safe]
interp eval $i {
    proc type {_ arg} {
        global vars varname
        set vars($varname,type) $arg
    }
    proc description {_ arg} {
        global vars varname
        set vars($varname,description) $arg
    }
    proc default {_ arg} {
        global vars varname
        set vars($varname,default) $arg
    }
    proc variable {name body} {
        global varname
        set varname $name
        eval $body
    }
}

lassign $argv filename varname
# Evaluate the file
interp invokehidden $i source $filename
# And extract the default argument of the given variable
puts [interp eval $i [list set vars($varname,default)]]

Sample usage:

$ ./getvar variables.tf account_id
0987654321

Upvotes: 0

dawg
dawg

Reputation: 103744

Given:

$ cat file
variable "vm_ami" {
  type        = string
  description = "ami image id used for setting up the vm"
  default     = "ami-5157cd3f"
}
variable "vm_mount_name" {
  type        = string
  description = "mount point for new device"
  default     = "xvdb"
  #default     = "xvdb"
}
variable "vm_device_name" {
  type        = string
  description = "device name for new drive"
  default     = "xvdb"
  #default     = "nvme1n1"
}
variable "elb_account_id" {
  type        = string
  description = "account id for elb"
  default     = "01234567890"
}
variable "account_id" {
  type        = string
  description = "Account number"
  default     = "0987654321"
}

You can use Perl to read the file as one string and use multi-line regex to parse the blocks:

perl -0777 -ne 'while (/^variable\h+"([^"]+)"([^}]+})/gm) {
                    if ($1 eq "account_id") {
                        $2=~m/\h+default\h+[^"]+"(\d+)/;
                        print $1;
                        }
                    }' file
0987654321  

Or this GNU awk works as well:

gawk  'BEGIN{ RS = "\n\\s*variable\\s" ;  FS="\n"} 
            /"account_id"/ {
                for (i=1;i<=NF; i++) {
                    if ($i~/\sdefault\s/) {
                        match($i, /^[^"]*"([^"]*)/, matches)
                        print matches[1]
                    }   
                }   
            } ' file

Upvotes: 1

Related Questions