Version 23 of command

Updated 2008-06-26 19:53:11 by lars_h

Richard Suchenwirth - Tcl is the Tool Command Language. Execution is done by splitting scripts into commands (by newline or semicolon). A command is a sequence of words, the first being the command name, the rest its arguments. (So far from Tcl syntax). A specialized mantra would say "everything is a command", even what other languages call "control structures" (if, while, for...) or "declarations" (global, proc, variable...).

All available command names at a given time (and namespace) are returned by

 info commands

They may have come from several sources:

  • C-implemented commands
  • Tcl-implemented procs, listed in info procs
  • Aliases for other commands, listed in interp aliases {}
  • Tk widgets use their pathname as command; you can test whether a string is a widget command with winfo exists $name
  • images also use their name as command name, listed in image names
  • OO systems like incr Tcl also use object names in the command position

The following proc returns the type of a command, or an empty string if the name is not a command:

 proc command'type2 name {
    foreach {result    condition} {
             unsourced {[info commands $name]=="" 
                        && [info exists ::auto_index($name)]}
             {}        {[info command $name]==""}
             proc      {[info procs $name]==$name}
             alias     {[lsearch [interp aliases {}] $name]>=0}
             image     {[lsearch [image names] $name]>=0}
             widget    {[winfo exists $name]}
             command   1
    } {if $condition {return $result}}
 } ;# RS

To introspect commands in other than the current namespace, walk the tree with namespace children.

Note that sometimes a developer confuses info procs and info commands. As the above piece of code demonstrates, there are independant uses for the two.


Cameron Laird pointed out: "The succinct command'type2 definition is slightly non-robust in the face of, for example,

  proc tcl* args {}

It's far from alone in that minor weakness, of course."

And there is a way to fix this: while [info proc] gets disturbed by the asterisk (and returns a number of proc names that start with tcl), other subcommands like [info args] don't do glob matching, so replacing the "proc" case above with

             proc      {![catch {info args $name}]}

fixes the problem. Thank you! (RS)

CL responds with a couple of observations: 1. This is minor stuff. There are applications in production that have far bigger robustness problems, yet have been satisfying end-users for years. I can't even bring myself to call this a problem--it's more of a footnote. 2. On the other hand, it interests and entertains me how often programs admit minor transformations that are strictly superior in the sense that they're as readable and succinct, while being "more correct" (satisfying over a wider input space, for example). I salute Richard for so quickly finding such a transformation in this case.


LV 2008-June 26: command'type2 has a requirement that Tk be loaded before you use it. Judious use of catch probably could handle the issue...

Lars H: Another problem is that alias names aren't normalized — interp alias faithfully records exactly the string used when creating the alias, but the $name used for the command can be very different. For example, if you do

  foreach ns {:: bar ::baz} {
     interp alias {} ${ns}::foo {} ::foopkg::dispatch $ns {some more data}
  }

then interp aliases will faithfully return

  ::::foo bar::foo ::baz::foo

and it is only these strings you can use with interp alias if you want to look up what the alias points to.


SS 30 Jun 2005:

schlenk just produced this procedure to check if a given command exists, it is in theory trivial but in pratice to get it right is not too simple, so maybe it's a good idea to put the proc here:

 proc cmd_exists {cmd} {
     string equal "" [namespace eval :: [list namespace which $cmd]]
 }

This above checks if the command is fully qualified basically (callable from the global namespace with the given name), and could be used for a callback. If you just want to see the fully qualified name, use a simpel namespace which.


Can't you just use info command?

$ tclsh

  % info command info
  info
  % info command nonsense
  %

schlenk Not if you have funny command names that are glob patterns like a proc named * or info*, as info command does not have an -exact switch.

LV well, technically you can:

 $ tclsh
 % proc info* {args} {return}
 % info command info*
 info info*
 % info command {info*}
 info info*

However, the info command returns _all_ the matching names in this case, so you have to be ready to deal with that. That's a bit annoying.

Lars H: It is perfectly possible to do an exact search by escaping all glob metacharacters in the string, e.g.

 proc cmd_exists_2 {cmd} {
    llength [info commands [string map {\\ \\\\ * \\* ? \\? [ \\[ ] \\]} $cmd]]
 }

Then whether this would be any faster than the namespace which form is another matter... I suspect it could be that they don't do exactly the same thing when it comes to not-fully-qualified names, and that depending on the problem one might want one or the other.

escargo 1 Jul 2005 - Maybe there is some argument for making all the commands that use glob matching by default also accept -exact (and even may -regexp). Using glob matching by default is a convenience, but more specific behavior should be allowed.

Making commands more uniform (by ensuring uniform matching behavior) seems like it would make the language more regular and easier to understand.

Lars H: Well, that sounds like a short summary of TIP#246 [L1 ].

NEM: You can always use either:

 expr {$cmd in [info commands]}
 lsearch -exact [info commands] $cmd