main script

The main script is the primary script that is executed by the interpreter, e.g. tclsh or wish.


One common technique is to have a script run a self test if it detects that it is the main script. Naive approaches only compare info script with $argv0. A somewhat more robust approach that usually works with pkg_mkIndex looks only at the file tail of those two values. The most robust approach fully normalizes those two values, including the last component, which a simple file normalize does not resolve.

The most complete approach:

# assumes the current level is #0 so there is no need to check the namespace of
# $argv0
if {[info exists argv0] 
        [file dirname [file normalize [file join [info script] ...]]]
        [file dirname [file normalize [file join $argv0 ...]]]
} {

    #do stuff

The ... on the end of each path ensures that file normalize resolves the path completely. Normally, the last component will not be resolved (see documentation for details).

The namespace qualifiers check ensures that the script is only considered the main script when the current namespace is the global namespace. Without this check, a script can falsely be considered the main script a second time if it uses info script to read itself.

A common and fairly robust approach:

if {[info exists argv0] && [file tail [info script]] eq [file tail $argv0]} {
    #do stuff

The negative approach, also quite common:

if {![info exists argv0] || [file tail [info script]] ne [file tail $argv0]} {

# test/standalone code follows

The more simple and naive approach to detection method directly compares info script with $argv0:

if {[info exists argv0] && $argv0 eq [info script]} {
    #do stuff

or, as formulated in Methods for Justifying text output in TCL , DKF, comp.lang.tcl, 2001-06-15:

if {[string equal $::argv0 [info script]] || [array exists ::embed_args]} {

AMG: Which posting would that be? What is ::embed_args? It must be something specific to the script DKF was describing.

PYK: I migrated this example and the verbiage from another page (can't remember which one now), and unfortunately it was just as unclear on that other page as well. I just searched comp.lang.tcl though, found the conversation, and included it above.

AMG: Here's a new way to do it, comparing device and inode numbers:

proc mainScript {} {
    global argv0
    if {[info exists argv0]
     && [file exists [info script]] && [file exists $argv0]} {
        file stat $argv0        argv0Info
        file stat [info script] scriptInfo
        expr {$argv0Info(dev) == $scriptInfo(dev)
           && $argv0Info(ino) == $scriptInfo(ino)}
    } else {
        return 0

And by request from PYK, here's how to make the above callable as [info main]:

namespace ensemble configure info -map [dict replace\
    [namespace ensemble configure info -map] main mainScript]

PYK: Actually, I was suggesting that since only tclsh itself really knows what is the main script, e.g. wouldn't get confused by source info script, that it should provide info main as an alternative to all the scripts on this page.

dbohdan 2014-10-11: It appears that if a script is run from a network drive on Windows $argv0Info(ino) == $scriptInfo(ino) doesn't hold.

PYK 2014-10-11: I just tried with Tcl version 8.6.2 under Windows XP, and didn't detect any problem. Can you provide more details?

dbohdan 2014-10-12: Sure. I happened upon this problem when testing 2048.tcl in a Windows XP VM in VirtualBox under Linux. To run the game more conveniently I mapped a shared folder to a networked drive letter (\\vboxsvr\2048-tcl to drive T:) but the game did not start when I tried to run it. Debugging the problem I found that in mainScript $argv0Info(ino) and $scriptInfo(ino) had different values, e.g., 60568 and 24584 or -28664 and 24584. This was true for both ActiveTcl 8.5 and tombert's tcltk 8.6.2. If you could not reproduce it then VirtualBox's shared folder mechanism rather than running from a network drive may be to blame.