'''Purpose: explain how best to start an executable script under Unix. From news:comp.lang.tcl ''' See also: [DOS BAT magic] ''But before you invest some of your precious time checking out what's in store at [DOS BAT magic], be informed that you can use [Cygwin]'s port of [sh] instead, and avoid some [MS-DOS] [Hell].'' ---- There seems to be a growing consensus that the best way to make a Tcl script executable as if it were a Bash or Sh script is to put the following on the very top line of your Tcl (or Tk) script: #!/usr/bin/env tclsh If you're using Tk, then you may want to invoke "wish" instead of "tclsh", like so: #!/usr/bin/env wish though even better is === #! /usr/bin/env wish package require Tk === In this way, not only would the script work with wish, but also tclsh's and tclkit's with GUI support. (Note however that loading Tk into a vanilla tclsh in all circumstances. For example, there is a "console only" version of tclkit that doesn't support Tk.) Note that the `#!` line must appear at the top of your Tcl program file, and the file must have execute permissions. The Tcl/Tk file can now be invoked just as if it were a command file, or a shell script written in Bash, sh, csh, et cetera. After seeing the first line, the shell should pass the rest of the script to tclsh/wish. The normal function of `/usr/bin/env` is to set one or more environment variables before calling some other program, but in this case we just call the other program (tclsh or wish) without setting any. The point of the whole thing is instead that `env` when calling the other program does so using a system call that looks for the named program in the directories of the PATH environment variable, just like a shell would. Therefore, the first "tclsh" (or "wish") command found in your current PATH will be the tclsh version used to run your Tcl script, and the program will have the exact same environment settings it would have if you'd invoked the script by typing "tclsh myfile.tcl" from your normal shell prompt. The "env" technique is a little surprising only because for a long time, the accepted way to invoke an executable Tcl script under Unix was to invoke "#!/bin/sh" with some special continuation lines and commands, as shown below. Further on down this wiki entry, you should also find a discussion of the "env" technique, and why it's better. If someone ever finds a problem with the "env" technique, please post it here. Otherwise, we should probably update all of the Tcl/Tk documention to deprecate the older (rather convoluted) continuation-line trick. Ah, but what if you have multiple versions of Tcl/Tk installed, and you want to invoke a particular version? You can do it by changing your PATH environment variable so that the preferred version is found first. That will affect every script which uses the same PATH value. If you know that a particular script needs a particular version of tclsh/wish, you could update the script so that the first line points to the version you want to execute instead of the first tclsh/wish found in PATH. For example, if you named the 8.5 version of wish as "wish8.5", you could change the first line to "#!/usr/bin/env wish8.5". [rdt] See my comment about the use of /usr/bin/env below. In some cases, the continuation-line trick still has some advantages over /usr/bin/env. Consider this clever-albeit-somewhat-obtuse scheme by [FPX]: : [FPX] The spiel of having a shell script at the beginning of your Tcl script can of course be extended as necessary. This is what I use to make my scripts run both on Unix and from within [Cygwin]'s bash shell on [Windows]. #! /bin/sh # the next line restarts using wish8.4 on unix \ if type wish8.4 > /dev/null 2>&1 ; then exec wish8.4 "$0" ${1+"$@"} ; fi # the next line restarts using wish84 on Windows using Cygwin \ if type wish84 > /dev/null 2>&1 ; then exec wish84 "`cygpath --windows $0`" ${1+"$@"} ; fi # the next line complains about a missing wish \ echo "This software requires Tcl/Tk 8.4 to run." ; \ echo "Make sure that \"wish8.4\" or \"wish84\" is in your \$PATH" ; \ exit 1 : This scheme could be extended to search the $PATH for any usable version of tclsh or wish. ---- Here's the original text about the continuation line trick, before /usr/bin/env became more popular: #!/bin/sh # This line continues for Tcl, but is a single line for 'sh' \ exec wish "$0" ${1+"$@"} (beginning of original message) Subject: Re: Documentation error (was: Why "$@" and not $@ ?) From: tromey@cygnus.com Date: 1998/07/07 Newsgroups: comp.lang.tcl John> But back to the topic ... Maybe it would help if we could get a John> good writeup of just how and why it works, and twist arms to John> install the description in various highly-visible places. I'd John> do it myself, but I'm not at all confident that I could write a John> correct description of it all. Maybe I should give it a try, and John> post it for criticism ... The incantation looks like this: exec wish "$0" ${1+"$@"} Here's how it works: Each argument to sh is numbered $1, $2, $3, ... $9. $0 is the name of the script. We put it in quotes to preserve whitespace and other weird characters. The special variable $* expands to all the arguments. However, it expands *unquoted*, meaning that spaces will be lost in the expansion. This means that the naive: exec wish "$0" $* will not work properly if any argument has a space in it. Likewise, using "$*" will fail because that will expand to a single argument. The special form "$@" was introduced (the quotes are part of it) to work around this problem. This form expands to all the arguments, but properly (and individually) quoted. So why doesn't the following attempt work? exec wish "$0" "$@" The reason is that some versions of sh have a bug. If there are no arguments, these broken sh's will still expand "$@" into a single empty argument. Oops. The sh syntax ${VAR+VALUE} expands to VALUE if $VAR is set, and nothing otherwise. We abuse this syntax to work around the buggy sh implementations. Recall that the first argument is named $1. If it exists, then we use "$@"; otherwise we do nothing: exec wish "$0" ${1+"$@"} The above is my interpretation. No doubt further refinements and clarifications are in order. Tom ---- [DKF] - On some platforms, it is important to put a space between the ''#!'' and the ''/bin/sh'' (or whatever) so that magic file numbering works. More sophisticated versions of this trick are needed when you've got a plethora of Tcl versions installed and your script cares which one it gets. These extensions are pretty much obvious to a shell programmer though... [LV] - note that the above is shell specific ; if you use something other than Bourne/korn shell, or older versions of these two, your experiences may differ in what works. Certainly if you move off into more '''futuristic''' shells, you may find that none of these features work. [RS] - But I think there is a habitual agreement that /bin/sh can always be expected, so it's safest to base portable scripts on that. [LV] - But at least on Linux these days, it is my understanding that /bin/sh is really either ksh or bash... if one finds the above magic doesn't work, just drop by here and let us know what is going on and perhaps someone can help find new magic for that platform. [DKF] - All unix shells come from two lineages: Bourne Shell and C-Shell. The two are almost completely incompatible with each other at a syntactic level (though most modern shells derive from both, at least feature-wise) so when you ask for /bin/sh, you will always get something that knows how to handle Bourne syntax (which is all the above trick depends on). Correspondingly, when you ask for /bin/csh, you always get something that knows C-Shell syntax. The put-a-space-after-the-#! trick is a deeply obscure feature of Unix kernel magic processing; you only get to find these things out by reading background material for autoconf... [willdye] According to a Bash expert, the trick should work for any version of any Bourne shell. If not, there's a bug in the shell. ---- More commentary appears in [http://www.python.org/cgi-bin/faqw.py?req=show&file=faq04.063.htp]. ---- [Donal Fellows] contributed in c.l.t. an extended version of the magic code, that can call either ''tclsh'' or ''wish'': #! /bin/sh # If the script's name starts with tk, use wish... \ case "`basename $0`" in *tk*) exec wish "$0" ${1+"$@"} # ... and otherwise use tclsh. \ ;; *) exec tclsh "$0" ${1+"$@"};; # That is all. \ esac ''[DKF] notes in passing:'' Did I? I don't remember that; it's really quite clever. I'm impressed with myself... ---- [Wish] has a few options like -s that it snatches from the command line before setting up ''argv''. If you want those to be visible for your script (and not for wish itself), insert doubledash (which ends wish's argument processing): exec wish "$0" -- ${1+"$@"} Note that if you do that, on Unix systems your script will no longer handle X windows options (like -geometry and -display) automatically. ---- [RS] found that the first line #!/usr/bin/env tclsh also works (starts the first tclsh in PATH) quite nicely on his Linux, Solaris, and Cygwin installations - so he will use this instead of ''exec magic'' in the future. Note that this actually does the same as using ''/bin/sh''. That trick (abusing the different comment handling of ''sh'' and ''tclsh'' '''is''' a trick) with ''/bin/sh'' does nothing else than setting up the environment (including PATH) so that ''tclsh'' is a valid command that actually runs something. If you look at ''man env'' you will see that ''env'' does nothing else than setting up the environment and execute the command given to it, along with remaining arguments. Using ''#!/usr/bin/env tclsh'' is just the '''correct way''' to achieve this (simple) goal. It is meant for exactly the task in question. - [joh] [rdt] 2006.07.15 - I'd say, joh, thats a matter of opinion. In my more that 2 decades of Unixy/Linux experience, I've seen env in at least four different situations: 1. not present on the machine, 1. only in /usr/bin, 1. only in /bin, 1. in both /bin and /usr/bin (usally as a link from one to the other). Now, the '''one''' common thing (but I'm sure there are exceptions), was that they '''all''' had a /bin/sh. So the probability of of the /usr/bin/env working is less than that of /bin/sh magic. If you, or others, have always found /usr/bin/env, then ''lucky you''. [SEH] 20060715 -- My experience has been the opposite. In work situations where I had to run scripts on a wide variety of Unices and versions, there were computers with no /bin/sh, but I never encountered one where env was not in /usr/bin. So discovering the env trick was a lifesaver for me. I guess it's best to know about both options and use what works out best for you. [Lars H], 2008-06-20: Under the [Filesystem Hierarchy Standard], `/bin/sh` is mandatory. The `env` program is not even mentioned. Interestingly enough, it also lists `/usr/bin` as the proper location for [tclsh], [wish], and [expect] (if they are at all installed). ---- Some people run into problems if the file was created under DOS first and then used on Linux. The problem is this. Say you have a file that looks like this: #! /bin/sh # This line continues for Tcl, but is a single line for 'sh' \ exec wish "$0" ${1+"$@"} or some variation. However, originating in DOS/Windows, the lines are all terminated by carriage return (^M character). When the user executes the file, s/he might get the error: ksh: /tmp/err.tcl: not found The trailing carriage return causes the command shell problems before it ever reaches tcl. The user will however think there's a tcl problem - so this is one of the things to check. ---- [RPR] (2006AUG27) Actually, in DOS/Windows, the lines in a pure text file terminate in carriage-return linefeed (^M^J, or hex 0D 0A). ---- I hope it is ok to edit this. I am a beginner at tcl and programming (half an hour's playing with it). However I found that I could get a gui tcl script (the example that was given for buttons) to run from a double click in konqueror on Mandrake 9.2 by right clicking on the file and chosing "open with" and then chosing "wish". I wondered if this would be useful information for other newbies? [FW]: Good to know, I suppose. Yes, you can edit anything you want - that's the point :) ---- [FW]: So let's be clear - does the ''#!/usr/bin/env tclsh'' method have any disadvantage whatever over exec magic? [MSW](2005-05-03): ''yup''. The "magic" is more portable. You can't count on env being installed. The "magic" has seen years of testing and wide deployment, you can trust it. You can't say the same about env (although supposedly the last dinosaurs which lack it are dying). [rdt] Yes, I prefer the ''magic''. See my comment about this use above. ---- [D. McC]: Almost as soon as I read this three weeks ago, I scrapped the "exec magic" at the beginning of the Tcl/Tk programs I've written and inserted ''#!/usr/bin/env wish'' at the beginning of my working versions instead. They work fine. SnackAmp, WaveSurfer, and FileRunner also have no problem with this line substituted for "exec magic" on my Mandrake Linux 9.2 system. The only disadvantage I see is that Konqueror no longer thinks these programs are shell scripts, so I can't run them with a double-click from Konqueror any more. I can easily live without that. So I say, good-bye forever to evil-looking exec magic! [LV] At the very least, if you are going to ignore the fact that env might not exist in /usr/bin, consider this change to what D. McC is doing - use #!/usr/bin/env tclsh package require Tk if you are using a Tcl from the past 5 years or so. That way, even if the person using the program is using a tclkit instead of tclsh, the program still works. ---- [RJ] 2005-05-03 - Here's something that may not belong on this page, but it's as close as I can get. Consider an application that will run on Solaris 8, Tcl v8.3+, Expect v5.31+ and Tk 8.3+. The application will also require the capability for the user to enter a "-d" on the command line as a *nix-like switch. For example: ''dothis -d tothat''. The obvious solution would be to use - #!/usr/bin/env expectk -- as the magic. This would load Tcl, Tk and Expect into the interpreter and pass whatever else is on the command line on to $argv, right? Right. It does, BUT it also passes the "-d" on to expectk as an argument. This results in: Application initialization failed: ambiguous option "-d" ..because expectk wants to see either -display or -diag. The expectk (at least as it's compiled here) ignores the "--" switch termination. [[Note that this occurs no matter which form of exec magic I use.]] I've tried everything to make this work with expectk and so far (this is a month now) I have only found one thing to make this work... I load Expect and Tcl with - #!/usr/bin/env expect -- ..which behaves correctly and ignores switches after the "--" passing them on to the application as $argv. But this still leaves me without Tk. As soon as I package require it, it dies with: Application initialization failed: "-d" option requires an additional argument ..I assume Tk is considering it the -display option. The workaround then became to parse the command line args to variables in the app first, set argv to null, then call Tk. Which even to my minimal scripting standards is an embarrassing hack. Is this a seriously flawed compilation or bug? ---- [JAB] 2005-09-20 - Here's something broken with exec magic replacement: the above #!/usr/bin/env expect -- doesn't work for me. (RHEL3, OS X) Env complains: "/usr/bin/env: expect --: No such file or directory" Obviously env isn't treating the -- as an argument to expect, so it's not doing what it should according to the man page or the above post. What system is it working correctly on for you? The standard magic doesn't do what I want to either - preserve user input to the script of -- such as "./test -- -foo". I modified it to #!/bin/sh #\ exec expect "$0" -- ${1+"$@"} And that does preserve my "--" argument to the script. ---- [AMG]: I prefer to make the continued comment line contain an explanation that the '''next''' line restarts with tclsh. This hopefully discourages people from separating the two. #!/bin/sh # The next line restarts with tclsh.\ exec tclsh "$0" ${1+"$@"} Also, here [http://www.eggheads.org/cgi-bin/viewvc.cgi/wolfpack/wolfpack.tcl?view=markup] is some exec magic to start with the "latest version of tclsh in PATH". Personally I don't see the need for this, but it is a complex example of exec magic. ---- [Setting up the .profile in the script] ---- !!!!!! %| [Arts and crafts of Tcl-Tk programming] | [Category Deployment] |% !!!!!!