Forum Discussion

Matt_Breedlove_'s avatar
Matt_Breedlove_
Icon for Nimbostratus rankNimbostratus
Jun 24, 2014

Help with Scan or String first last and multiple/last occurence

Need some help trying to always match the string immediately preceding the first period and delimited by the leading dash

So given these hostnames, the desired match is indicated...I would be wanting the scan or string command to always return the second string


prod-tools-discovery.acme.com should result in discovery
prod-tools.acme.com should result in tools
prod-io-tools.acme.com should result in tools
stg-io-tools-config.acme.com should result in config
stg-io-tools.acme.com should result in tools
etc

I thought I could do a string reverse on the hostname, split on the last period, then read until the first dash and reverse the result, but irules does not allow "string reverse" I looked at scan and thought that would work, but I can't see how to tell scan to look for the last occurrence of a dash and then read until the first occurrence of a period.

I prefer scan, but if that is not possible can the string commands support skipping to after the last occurrence of a certain character?

5 Replies

  • I don't think you necessarily can use scan, because you won't know how many dashed values there are before the period. This is what I might do:

    set test [list]
    lappend test "prod-tools-discovery.acme.com"
    lappend test "prod-tools.acme.com"
    lappend test "prod-io-tools.acme.com"
    lappend test "stg-io-tools-config.acme.com"
    lappend test "stg-io-tools.acme.com"
    
    foreach x $test {
        log local0. "input = [lindex [split $x "."] 0]"
        set a [lindex [split $x "."] 0]
        set b [lindex [split $a "-"] [expr [llength [split $a "-"]] -1]]
        log local0. $b
    }
    

    I suppose you technically could use scan, but I think it'd be more troublesome:

    set test [list]
    lappend test "prod-tools-discovery.acme.com"
    lappend test "prod-tools.acme.com"
    lappend test "prod-io-tools.acme.com"
    lappend test "stg-io-tools-config.acme.com"
    lappend test "stg-io-tools.acme.com"
    
    foreach x $test {
        catch { unset a b c d }
        scan [lindex [split $x "."] 0] {%[^-]-%[^-]-%[^-]-%s} a b c d
        if { [info exists d] } { 
            log local0. $d
        } elseif { [info exists c] } {
            log local0. $c
        } elseif { [info exists b] } {
            log local0. $b
        } else {
            log local0. $a
        }
    }   
    
  • Hi

    How about this one?

    set httphost "prod-discovery.acme.com"
    set hostarr [lindex [split $httphost "-"] end]
    set thepartyouwanted [lindex [split $hostarr "."] end-2]
    

    /Patrik

  • This is a derivative of what Patrik suggested that I came up with as a result of also in some cases needing to match the second to last field of dash delimiters in a hostname

    I think this is the best way to do this with lindex because it removed the dependency on the number of periods in the original hostname. Now the only dependency is that the target part of the hostname containing dashes is on the leftmost side of the hostname

    So to find the last field of a dash delimited string in an HTTP::host

    
    set httphost "test-tools-config.acme.com"
    set a [string tolower [lindex [split [lindex [split [getfield $httphost ":" 1] "."] 0] "-"] end]]
     a = config
    

    And to find the second (or N) to last field of a dash delimited string in HTTP::host

    
    set httphost "test-tools-config.acme.com"
    set b [string tolower [lindex [split [lindex [split [getfield $httphost ":" 1] "."] 0] "-"] end-1]]
     b = tools
    

    The only reason that lindex can do this with variable field delimiters where scan, string, etc fail, is that it allows the "end" and "end-N" operator

    Honestly, I think a built in function is needed in irules similar to "getfield", but would be "reverse" to reverse a string. I believe native TCL has a "string reverse", but it is not enabled for some reason in irules : (. Perhaps it doesnt gain mutch anywa since with lindex I must do two splits and two lindex's and one getfield to accomplish this. With a reverse function it would be just less lindex's and more reverse's The goal was really to use SCAN, but unless irules is going to allow a custom flag in SCAN similar to "end" and "end-N" then I guess it just all boils down to string/array functions

  • You could also use a regex:

    set str "test-tools-config.acme.com"
    set foo [string range [regexp -inline {\-[^-.]+\.} $str] 1 end-1]
    log local0. $foo
    

    Grab the part of the string that starts with a dash, ends with a period, and does not contain either in the middle, then strip the dash and period from the resulting string.

  • You're probably right from a performance perspective:

    !/usr/bin/tclsh
    
    set str "test-tools-config.acme.com"
    
    set timer1 [time {
        set foo [string range [regexp -inline {\-[^-.]+\.} $str] 1 end-1]
    } 10000]
    puts $timer1
    
    set timer2 [time {
        set foo [string tolower [lindex [split [lindex [split $str "."] 0] "-"] end-1]]
    } 10000]
    puts $timer2
    

    On my lab VM the regex is always a bit heavier