Forum Discussion

amazingf1_19652's avatar
amazingf1_19652
Icon for Nimbostratus rankNimbostratus
Nov 18, 2016

Variable substitution in switch statement not working

variable substitution in switch statement is not working. For example here is the sample code. I tried removing the braces and put a \'' at end of each case, but no luck. My LTM is 10.x version. when HTTP_REQUEST { set d1 [HTTP::path] switch [string tolower [HTTP::path]] {

 

    "/path1" -
    "/path2" {
        pool www
    }

    "$d1" {
        HTTP::respond 302 Location "https://google.com"
    }

    default { 
        HTTP::respond 302 Location "https://mail.google.com"
    }
}

}

 

  • From the Tcl man page: https://www.tcl.tk/man/tcl8.4/TclCmd/switch.htm:

    Two syntaxes are provided for the [switch] pattern and body arguments. The first uses a separate argument for each of the patterns and commands; this form is convenient if substitutions are desired on some of the patterns or commands. The second form places all of the patterns and commands together into a single argument; the argument must have proper list structure, with the elements of the list being the patterns and commands. The second form makes it easy to construct multi-line switch commands, since the braces around the whole list make it unnecessary to include a backslash at the end of each line. Since the pattern arguments are in braces in the second form, no command or variable substitutions are performed on them; this makes the behavior of the second form different than the first form in some cases.

    This gets into the guts of Tcl. Every statement in Tcl is a command. A command always follows the same pattern, namely:

    command ?arg? ?arg? ...

    This includes things that are normally built-ins for other languages. Take if, for example. Normally, you'd write it like this:

     

    if { $a eq "foo" } {
       ; do something...
    } else {
       ; do something else ...
    }
    

     

    It turns out that, in Tcl, the squirly braces ({}) are always non-interpolating quote operators. They specifically are not block delimiters. Non-interpolating quotes take away the special meaning of characters ... including the newline character. So in reality, that entire code block is effectively on one line and looks like this:

     

    if { $a eq "foo" } { ... } else { ... }
    

     

    So it is a command (if) followed by four arguments. As weird as it may seem to programmers from other languages, else, in this construct, is an argument. (Side note: for iRules, the developers hacked this a bit so that you can do uncuddled if...else blocks, but that's an iRules thing, not a Tcl thing).

    So, considering just Tcl, the following:

     

    set x "foo"
    switch "foo" { "abc" { puts "abc" } "$x" { puts "x" } default { puts "default" } }
    

     

    would print "default" because $x is not expanded in this form. However, this:

     

    set x "foo"
    switch "foo" "abc" { puts "abc" } $x { puts "x" } default { puts "default" }
    

     

    would print "x".

    There is another form that work, namely:

     

    set x "foo"
    switch foo \
       "abc" { puts "abc" } \
       $x { puts "x" } \
       default { puts "default" }
    

     

    because the backslash takes away the meaning of the newline following it. A newline is normally a statement separator. By taking away the special meaning, this again ends up effectively on one line.

  • I sent that last answer in something of a hurry and realized there are a few nuances that may further help.

    The switch command searches the parameter list for any flag arguments (e.g., "-glob"), processes them, extracts the match condition, then looks to see how many parameters remain. If it is one, then it treats it the usual way, which means no case expansion. Remember from the discussion above that if the switch looks like this:

     

    switch [HTTP::path] {
        "/foo/bar" {
            ...
        }
        
        "/etc/etc/etc.html" {
            ...
        }
    }
    

     

    then the switch command has two parameters; namely, the match condition ([HTTP::path]), then all of the contents of the squirly braces.

    Alternatively, if, after removing the flags and the match condition, there is more than one parameter remaining (strictly speaking, there must be an even number), then it treats it in the second way; that is, it treats the parameters as a series of case+branch pairs and expands each of the cases.

    This leads to the second subtlety. In general, if you use the second form, it's a good idea to put the flags first, then a double-dash, then the cases, like this:

     

    switch -glob [HTTP::path] -- \
        "$x/*" { \
            log local0. "Matched x: [HTTP::path]" \
            HTTP::path "/new_path.html" \
        } \
        "/foo/bar/*" - \
        "/baz/bing.html" { \
           ... \
        }
    

     

    The double-dash instructs switch to stop looking for flags after that point. This is defensive. Let's say one of your cases was "-yadda". switch would panic because it wouldn't recognize this "flag". By using the double-dash (--) before that case, it wouldn't try to interpret "-yadda" as a flag, but rather, treat it as a case.

    I know this is a very long answer to your very short question, but I hope that it is helpful.

    • amazingf1_19652's avatar
      amazingf1_19652
      Icon for Nimbostratus rankNimbostratus

      Hi Vernon, Thanks for your much informative and elaborate answers. Appreciate it. I earlier tried to put the "\" at end of each case but gave me syntax error. your answer puts \ at the end of each line even inside the case. I am not sure if its feasible for me as I have some 200 lines of code in the switch statement, which includes another switch in one or more cases, and to keep \ at end of each line. I will try this out and see if that removes the syntax errors.

       

  • Hi Vernon, Thanks for your much informative and elaborate answers. Appreciate it. I earlier tried to put the "\" at end of each case but gave me syntax error. your answer puts \ at the end of each line even inside the case. I am not sure if its feasible for me as I have some 200 lines of code in the switch statement, which includes another switch in one or more cases, and to keep \ at end of each line. I will try this out and see if that removes the syntax errors.

     

  • Keep in mind that the backslashes are needed specifically because the enclosing quoting operators ({...}) have been removed, so don't forget to do that, as well. I'm curious, however, about the general cases you are using. Perhaps you can significantly reduce the set by using one or more data groups?