Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Why don't variables work in switch patterns?

971 views
Skip to first unread message

Todd A. Jacobs

unread,
Nov 10, 2006, 4:29:31 PM11/10/06
to
I have a snippet here that works:

set RegExFrom {^ >> From: }
set RegExSubj {^ >> Subj: }
set pipe [open "|tmda-pending -bs" r]
while { [gets $pipe line] >= 0 } {
switch -regexp -- $line {
{^\d+} { regsub -all {^([^\s]+).*} $line {\1} MsgID }
{^ >> From: } { regsub $RegExFrom $line {} From }
{^ >> Subj: } { regsub $RegExSubj $line {} Subj }
{^<} {
set MsgArray($MsgID) [list $From $Subj]
unset MsgID From Subj
}
default continue
}
}
close $pipe

but only because I'm not using a variable in the switch pattern. The
variables work in the body, but not in the pattern. Can anyone help me
understand why?

--
Unabashedly littering the information superhighway with detritus like
this for over 15 years now.

Bryan Oakley

unread,
Nov 10, 2006, 4:51:26 PM11/10/06
to
Todd A. Jacobs wrote:
> I have a snippet here that works:
>
> set RegExFrom {^ >> From: }
> set RegExSubj {^ >> Subj: }
> set pipe [open "|tmda-pending -bs" r]
> while { [gets $pipe line] >= 0 } {
> switch -regexp -- $line {
> {^\d+} { regsub -all {^([^\s]+).*} $line {\1} MsgID }
> {^ >> From: } { regsub $RegExFrom $line {} From }
> {^ >> Subj: } { regsub $RegExSubj $line {} Subj }
> {^<} {
> set MsgArray($MsgID) [list $From $Subj]
> unset MsgID From Subj
> }
> default continue
> }
> }
> close $pipe
>
> but only because I'm not using a variable in the switch pattern. The
> variables work in the body, but not in the pattern. Can anyone help me
> understand why?
>

Why? That's just the way it is designed to work.

You can use the alternate form of switch, which is

switch <options> pattern body ?pattern body?

For example:

switch -regexp -- $line \
$pattern1 {
....
} \
$pattern2 {
....
}

Note that you have to use lots of continuation lines for this method,
but it does allow substitutions on the patterns. You can mostly avoid
that if you put the patterns on the same line as the end of the previous
body:

switch -regexp -- $line \
$pattern1 {
....
} $pattern2 {
....
} $pattern3 {
....
}
}

Robert Heller

unread,
Nov 10, 2006, 5:17:09 PM11/10/06
to
At Fri, 10 Nov 2006 13:29:31 -0800 "Todd A. Jacobs" <spam.y...@localhost.localdomain> wrote:

>
> I have a snippet here that works:
>
> set RegExFrom {^ >> From: }
> set RegExSubj {^ >> Subj: }
> set pipe [open "|tmda-pending -bs" r]
> while { [gets $pipe line] >= 0 } {
> switch -regexp -- $line {
> {^\d+} { regsub -all {^([^\s]+).*} $line {\1} MsgID }
> {^ >> From: } { regsub $RegExFrom $line {} From }
> {^ >> Subj: } { regsub $RegExSubj $line {} Subj }
> {^<} {
> set MsgArray($MsgID) [list $From $Subj]
> unset MsgID From Subj
> }
> default continue
> }
> }
> close $pipe
>
> but only because I'm not using a variable in the switch pattern. The
> variables work in the body, but not in the pattern. Can anyone help me
> understand why?

How *exactly* are you quoting things????


I would expect this to work:

set RegExFrom {^ >> From: }
set RegExSubj {^ >> Subj: }
set pipe [open "|tmda-pending -bs" r]
while { [gets $pipe line] >= 0 } {
switch -regexp -- $line {
{^\d+} { regsub -all {^([^\s]+).*} $line {\1} MsgID }

"$RegExFrom" { regsub $RegExFrom $line {} From }
"$RegExSubj" { regsub $RegExSubj $line {} Subj }


{^<} {
set MsgArray($MsgID) [list $From $Subj]
unset MsgID From Subj
}
default continue
}
}
close $pipe

But not this:

set RegExFrom {^ >> From: }
set RegExSubj {^ >> Subj: }
set pipe [open "|tmda-pending -bs" r]
while { [gets $pipe line] >= 0 } {
switch -regexp -- $line {
{^\d+} { regsub -all {^([^\s]+).*} $line {\1} MsgID }

{$RegExFrom} { regsub $RegExFrom $line {} From }
{$RegExSubj} { regsub $RegExSubj $line {} Subj }


{^<} {
set MsgArray($MsgID) [list $From $Subj]
unset MsgID From Subj
}
default continue
}
}
close $pipe

>

--
Robert Heller -- 978-544-6933
Deepwoods Software -- Linux Installation and Administration
http://www.deepsoft.com/ -- Web Hosting, with CGI and Database
hel...@deepsoft.com -- Contract Programming: C/C++, Tcl/Tk

Bryan Oakley

unread,
Nov 10, 2006, 5:39:07 PM11/10/06
to
Robert Heller wrote:
> I would expect this to work:
> ...

> switch -regexp -- $line {
> {^\d+} { regsub -all {^([^\s]+).*} $line {\1} MsgID }
> "$RegExFrom" { regsub $RegExFrom $line {} From }

Expect to be disappointed. That's not how the switch command works.

Donal K. Fellows

unread,
Nov 10, 2006, 6:19:14 PM11/10/06
to
Bryan Oakley wrote:
> You can use the alternate form of switch, which is
> switch <options> pattern body ?pattern body?

Note that this case of [switch] syntax is *precisely* intended for use
with variables containing patterns (and command-substitutions returning
patterns, and other more complex stuff). The other form is only really
for the (OK, massively common) case of static patterns.

Donal.

Bruce

unread,
Nov 10, 2006, 6:43:34 PM11/10/06
to
Robert Heller wrote:
> At Fri, 10 Nov 2006 13:29:31 -0800 "Todd A. Jacobs" <spam.y...@localhost.localdomain> wrote:
>
>> I have a snippet here that works:

>> but only because I'm not using a variable in the switch pattern. The


>> variables work in the body, but not in the pattern. Can anyone help me
>> understand why?
>
> How *exactly* are you quoting things????
>
>
> I would expect this to work:
>
> set RegExFrom {^ >> From: }
> set RegExSubj {^ >> Subj: }
> set pipe [open "|tmda-pending -bs" r]
> while { [gets $pipe line] >= 0 } {
> switch -regexp -- $line {
> {^\d+} { regsub -all {^([^\s]+).*} $line {\1} MsgID }
> "$RegExFrom" { regsub $RegExFrom $line {} From }
> "$RegExSubj" { regsub $RegExSubj $line {} Subj }
> {^<} {
> set MsgArray($MsgID) [list $From $Subj]
> unset MsgID From Subj
> }
> default continue
> }
> }
> close $pipe
>

and your expectations would not be met.

look at the forth argument to your switch statement. it
is quoted with {} which means that *no* substituion will occur
just becaus there are quotes inside of it don;t make a difference
to parser - that outer word is quoted with {} so the whole thing is
passed as is to the switch statement - which doesn't do any extra
substitutions on the pieces.

The right answer is to use the form of switch that takes the pattern
and action as sepearate arguments, not all lumped together, then the
parser will expand them as needed (depending on quoting of course)

Bruce

Cameron Laird

unread,
Nov 10, 2006, 7:12:49 PM11/10/06
to
In article <fc75h.6712$yl4....@newssvr12.news.prodigy.com>,

Now I'm confused. Bryan, what do you expect of

set value abcdefgh...wxyz

switch -regexp -- $value {
"ab.*xyz" {
puts Success.
}
default {
puts Failure.
}
}

? Maybe I've lost track of the antecedent for the
"That" in your last sentence.

Bryan Oakley

unread,
Nov 10, 2006, 9:00:55 PM11/10/06
to
Cameron Laird wrote:
> In article <fc75h.6712$yl4....@newssvr12.news.prodigy.com>,
> Bryan Oakley <oak...@bardo.clearlight.com> wrote:
>
>>Robert Heller wrote:
>>
>>>I would expect this to work:
>>>...
>>> switch -regexp -- $line {
>>> {^\d+} { regsub -all {^([^\s]+).*} $line {\1} MsgID }
>>> "$RegExFrom" { regsub $RegExFrom $line {} From }
>>
>>Expect to be disappointed. That's not how the switch command works.
>
>
> Now I'm confused. Bryan, what do you expect of
>
> set value abcdefgh...wxyz
>
> switch -regexp -- $value {
> "ab.*xyz" {
> puts Success.
> }
> default {
> puts Failure.
> }
> }
>

Success. The question was about variables in the patterns; your example
doesn't include variables in the patterns.

Kaitzschu

unread,
Nov 10, 2006, 9:00:31 PM11/10/06
to
On Sat, 11 Nov 2006, Cameron Laird wrote:

> In article <fc75h.6712$yl4....@newssvr12.news.prodigy.com>,
> Bryan Oakley <oak...@bardo.clearlight.com> wrote:
> >Robert Heller wrote:
> >> I would expect this to work:
> >> ...
> >> switch -regexp -- $line {
> >> {^\d+} { regsub -all {^([^\s]+).*} $line {\1} MsgID }
> >> "$RegExFrom" { regsub $RegExFrom $line {} From }
> >
> >Expect to be disappointed. That's not how the switch command works.
>
> Now I'm confused. Bryan, what do you expect of
>
> set value abcdefgh...wxyz
>
> switch -regexp -- $value {
> "ab.*xyz" {
> puts Success.
> }
> default {
> puts Failure.
> }
> }

That would work (everything is literal inside switch). But
set patt "ab.*xyz"
set value abcdefgh...wxyz

switch -regexp -- $value {
"$patt" {


puts Success.
}
default {
puts Failure.
}
}

would not as outer braces (the ones starting after $value) would prevent
$patt from being substituted (thus switch would compare _value_ of value
to _literal_ "$patt"). That is "That".

> ? Maybe I've lost track of the antecedent for the "That" in your last
> sentence.

--
-Kaitzschu
s="TCL ";while true;do echo -en "\r$s";s=${s:1:${#s}}${s:0:1};sleep .1;done

Ralf Fassel

unread,
Nov 11, 2006, 6:22:31 AM11/11/06
to
* Bruce <ne...@nowhere.org>

| to parser - that outer word is quoted with {} so the whole thing is
| passed as is to the switch statement - which doesn't do any extra
| substitutions on the pieces.

The confusion might come from the fact that you _can_ use variables
inside the actions, but not in the patterns, if the {} form is used.

Eg.
switch -- $value {
foo {
# can use variables here, but not in the pattern line directly above
puts "value is $value"
}
}

If you need substitution in the patterns, use the form of switch with
separate arguments for patterns and actions

switch -- $value \
$patt1 {
} \
$patt2 {
}

R'

Bruce Hartweg

unread,
Nov 12, 2006, 4:25:25 PM11/12/06
to
Ralf Fassel wrote:
> * Bruce <ne...@nowhere.org>
> | to parser - that outer word is quoted with {} so the whole thing is
> | passed as is to the switch statement - which doesn't do any extra
> | substitutions on the pieces.
>
> The confusion might come from the fact that you _can_ use variables
> inside the actions, but not in the patterns, if the {} form is used.
>
> Eg.
> switch -- $value {
> foo {
> # can use variables here, but not in the pattern line directly above
> puts "value is $value"
> }
> }

But that is clearly stated in the man pages. In this case the interpreter
does NOT substitute in either the pattern or bodies, they are passed literally
to the switch command which does t he following:

The switch command matches its string argument against each of the pattern arguments in order. As
soon as it finds a pattern that matches string it evaluates the following body argument by passing
it recursively to the Tcl interpreter and returns the result of that evaluation.

(quoted from http://www.tcl.tk/man/tcl8.4/TclCmd/switch.htm)

so the is no magic goping on - initially NO substitution occurs, then is a pattern
is matched the body associated is passed again to the interpreter - this is when
the variables are/commands are substituted.

Bruce

Ralf Fassel

unread,
Nov 13, 2006, 5:44:47 AM11/13/06
to
* Bruce Hartweg <bruce...@hartweg.us>

| The switch command matches its string argument against each of the
| pattern arguments in order. As soon as it finds a pattern that
| matches string it evaluates the following body argument by passing
| it recursively to the Tcl interpreter and returns the result of that
| evaluation.
|
| (quoted from http://www.tcl.tk/man/tcl8.4/TclCmd/switch.htm)

More to that (cited from same source):

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.

| so the is no magic goping on - initially NO substitution occurs,
| then is a pattern is matched the body associated is passed again to
| the interpreter - this is when the variables are/commands are
| substituted.

Yup. The prolonged existence of this whole thread raises the
question: how to make people (like me) who think they know TFM
RTFMAAAน ;-)

R'
---
น RTFM _A_gain _A_nd _A_gain...

aamirsa...@gmail.com

unread,
Aug 28, 2013, 10:00:07 AM8/28/13
to
Hit this issue myself until I looked up the man pages,
actually there are two syntax types, one of them supporting variable and command substition.

Gerald W. Lester

unread,
Aug 28, 2013, 7:12:34 PM8/28/13
to
Actually, both syntaxes support all substitution -- in the case of where
switch takes a list of duples, you just have to be real careful about how
you build the list.

BTW, the man page is not 100% correct when it says in the SYNOPSIS that the
syntax is:
switch ?options? string {pattern body ?pattern body ...?}

A detailed reading of the DESCRIPTION will actually reveal that the true
syntax is:
switch ?options? string listOfDuples

Where listOfDuples is a list consisting of an even number of elements where
the even indexed elements (assuming you view 0 as even) are a pattern and
the odd index elements are a body to execute.


--
+------------------------------------------------------------------------+
| Gerald W. Lester, President, KNG Consulting LLC |
| Email: Gerald...@kng-consulting.net |
+------------------------------------------------------------------------+

Donal K. Fellows

unread,
Aug 28, 2013, 7:45:57 PM8/28/13
to
On 28/08/2013 15:00, aamirsa...@gmail.com wrote:
> On Saturday, 11 November 2006 02:59:31 UTC+5:30, Todd A. Jacobs wrote:
>> I have a snippet here that works:
[...]
>> but only because I'm not using a variable in the switch pattern. The
>> variables work in the body, but not in the pattern. Can anyone help me
>> understand why?
>
> Hit this issue myself until I looked up the man pages, actually there
> are two syntax types, one of them supporting variable and command
> substition.

Formally, they both accept substitutions as supported by Tcl's syntax,
but the one that people interpret as a single braced thing is actually a
Tcl list of even length. Use braces to create it? No substitutions, as
normal when you use braces. The “body” arguments are then passed through
[eval] (well, Tcl_EvalObjEx really) if the relevant pattern matches,
which is why variable substitutions work inside. You could use [list] to
build it all up, but that would actually be more ugly than the
many-value version of [switch] which is indeed the recommended thing to
use when creating a [switch] with non-constant patterns.

Tcl's immediate metasyntax is *very* regular. Everything has the same
rules, so you can learn them once and use them all the time. (Forgetting
this basic rule is probably the #1 cause of slowdown in Tcl scripts “in
the wild”, which is silly because it is so simple.)

Donal.
--
Donal Fellows — Tcl user, Tcl maintainer, TIP editor.

Ya Hu

unread,
May 18, 2021, 5:16:47 AM5/18/21
to
proc case args {
foreach "1 2" [lindex $args end] {
append b " $1" " \{ $2\}"
}
uplevel 1 switch [lrange $args 0 end-1] $b
return
}
0 new messages