1

I have a string like

set arr_set(variable.abc) {12,13}
set arr_set(variable.def) {15,16}
set arr_set(dont_care)    {0,0}

where arr_set is an array set, and variable_abc is an element of it. I have all this information stored in a file. What I want to do is, read each line, and wherever I see "variable." I read what it is pointing to, like in first case, it is pointing to abc, and then def.

I wrote this script, but it is apparently not storing the information.

regexp -nocase {^\s*set arr_set[(]variable\s*.\s*(.*)$} $lines match tag value

where lines is the string that contains

set arr_set(variable.abc) {12,13}

I cant figure out what is the regex missing here. Can anyone take a look?

2 Answers2

2

Break down your regular expression:

^\s*set arr_set[(]variable\s*.\s*(.*)$
  • ^ : beginning of line anchor
  • \s* : zero or more spaces
  • set arr_set[(]variable : match this text exactly
  • \s* : zero or more spaces
  • . : any character
  • \s* : zero of more spaces
  • (.*) : the rest of the data

Your regex doesn't have separate groups for tag and value, though from your code, you appear to want them separated. You have extra space matching that doesn't match your data.

set lines {set arr_set(variable.abc) {12,13}}
regexp -nocase {\(variable\.([^)]*)\)\s*(.*)} $lines match tag value
puts "$tag $value"

Or

regexp -nocase {^\s*set\s*arr_set\(variable\.([^)]*)\)\s*(.*)} $lines match tag value

Break down:

\(variable\.([^)]*)\)\s*(.*)
  • \(variable\. : match this text exactly
  • ( : begin group (tag)
  • [^)]* : zero or more characters that are not a close parentheses
  • ) : end group (tag)
  • \) : close parentheses (exact match)
  • \s* : zero or more spaces
  • (.*) : the rest of the data (value)
Brad Lanam
  • 5,192
  • 2
  • 19
  • 29
  • Nice solution. But I only care about the variable it is pointing to, but with your solution it prints out everything after it matches variable. I am wondering if we can use ")" for it to stop looking – user3491702 Jan 11 '16 at 20:34
  • From your question, I had made the assumption that you wanted to keep the value. If you don't want the value, simply remove the `\s*(.*)` from the regular expression, and remove the `value` argument also. – Brad Lanam Jan 11 '16 at 20:37
  • Take a look at Peter's solution also. It is a more generic method of pulling out a specific set of names from `[array names ...]`. This would be the preferred solution. It's hard for us to tell what is best without knowing more about your use case. – Brad Lanam Jan 11 '16 at 20:40
  • All i need to do is extract out all the names that are prefixed with "variable", like I need abc, and def in above example. With peter's solution, I cannot use lmap because tcl version I am using doesnt support it seems. And I cant upgrade. – user3491702 Jan 11 '16 at 20:42
  • Well, `lmap` is not a requirement. I'll write a note to Peter, have him do an edit. – Brad Lanam Jan 11 '16 at 20:44
2

(Glenn Jackman reminded me in a comment that array names can take a glob pattern to select names. Since that feature simplifies the code significantly in this case, I've rewritten my answer to use it.)

If you have the commands

set arr_set(variable.abc) {12,13}
set arr_set(variable.def) {15,16}
set arr_set(dont_care)    {0,0}

in a file which you source, you can use this to get a list of tag-value pairs:

lmap name [array names arr_set variable.*] {
    set tag [lindex [split $name .] 1]
    list $tag $arr_set($name)
}

# => {abc 12,13} {def 15,16}

(If you don't want the value, use set tag instead of list $tag $arr_set($name))

For Tcl 8.5 and earlier, this does the same (storing the result in res):

set res {}
foreach name [array names arr_set variable.*] {
    set tag [lindex [split $name .] 1]
    lappend res [list $tag $arr_set($name)]
}

(If you don't want the value, use lappend res $tag instead of lappend res [list $tag $arr_set($name)])

Note that this solution only works if the array name selection can be expressed as a glob pattern. In cases where that isn't possible, a solution along these lines is still necessary:

lmap name [array names arr_set] {
    lassign [split $name .] prefix tag
    if {$prefix in {foo bar baz}} {
        list $tag $arr_set($name)
    } else {
        continue
    }
}

Documentation: array, continue, foreach, if, lappend, lassign, lindex, list, lmap, lmap replacement, set, source, split

Peter Lewerin
  • 13,140
  • 1
  • 24
  • 27