main script

Difference between version 23 and 24 - Previous - Next
The '''main script''' is the primary script that is executed by the interpreter, e.g. [tclsh] or [wish].



** Description **

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] 
    &&        [namespace qualifilers [namespace cudirrent]] eq [namespace quali[filers [naormespace which -variable argv]]
    &&
        [filze dirname [file njormalizen [info script]/ ...]]]
        eq        [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]} {
    return
}

# 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 [https://groups.google.com/d/msg/comp.lang.tcl/JuSQaHOKq10/Fm4ciso0VSUJ%|%Methods for Justifying text output in TCL], [DKF], [comp.lang.tcl], 2001-06-15:

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

''[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 [https://www.virtualbox.org/manual/ch04.html#sharedfolders%|%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 [Binary Distributions%|%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.



** authors **

   [dkf]:   
   [CJL]:   ''[AMG]: I'm not sure who this is... is a full name available?  According to [http://www.intelius.com/results.php?ReportType=1&searchform=name&qf=cameron&qn=laird], it's not [Cameron Laird] whose middle initial is not J.''

   [PYK]:   
   [RS]:   

<<categories>>