2
#!/bin/sh
# A Tcl comment, whose contents don't matter \
exec tclsh "$0" "$@"

why should we invoke shell here (#!/bin/sh) .

We can directly invoke the tclsh (#!/usr/sbin/tclsh) . Let us Assume tclsh is in sbin directoey.

Why we are First calling the shell and again inside the shell we are calling tclsh interpreter .

why people prefer to use these (#!/bin/sh , exec tclsh "$0" "$@") . Can't we execute tclsh direclty ?

velpandian
  • 431
  • 4
  • 11
  • 23

5 Answers5

3

For one reason, on some systems, you can rigidly lock down what executables can be run in a shebang line. In other words, it may refuse to run anything not in /bin. Or maybe, if your administrators are particularly sadistic, they may try to force everyone to use zsh :-)

Secondly, this will run tclsh based on your current path settings. That's invaluable if you want different users to run it with different versions of TCL. Otherwise, you have to give an absolute path on the shebang line and this may be different on different systems (although /usr/bin/env can also take care of that).

It's also handy to test the scripts with more recent versions of tclsh before committing to them. For example, you can have tclsh 8.5 under the current latest Debian (7.1) while testing TCL 8.6 by building it in $HOME/staging/tcl86 and changing your path so that directory appears before usr/bin.

On my system (which is locked down to my specs but no further), the script:

#!/usr/bin/env tclsh
puts $tcl_version

works just fine, outputting:

8.5

So does the script:

#!/usr/bin/tclsh
puts $tcl_version

although, as mentioned, it's tied to the system version of tclsh rather than the first one on my path.

And, yes, you can get at the arguments just fine, as per the following transcript:

pax> cat qq.tcsh
#!/usr/bin/tclsh
puts $argc
puts $argv

pax> qq.tcsh my name is pax
4
my name is pax
paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
  • So we can use directly tclsh right ? then argv0 , argv , argc variables can be accessible directly to the script ? – velpandian Sep 13 '13 at 08:32
  • @user2746819, it depends on how your system is set up. See the update although, if your system has higher levels of "security", it may not work. – paxdiablo Sep 13 '13 at 09:10
  • @paxdiablo There is a typo in your comment: It must mean "stupidy". – Johannes Kuhn Sep 13 '13 at 13:00
  • I remember there being some systems that would always run executable scripts in `csh`. That's just _so_ wrong on so many levels… – Donal Fellows Sep 13 '13 at 14:04
1

When you have a script like this, and you run it from the command line, the system thinks it is a shell script and will start to run each line as if it were a shell command.

$0 and $@ are shell variables.. $0 for tcl script path and ${1+$@} means "all the arguments, if the first argument is set" hence,

 exec tclsh $0 ${1+$@} 

means after stopping shell, execute tclsh by passing script file (via $0) and rest arguments (via ${1+$@})

When tclsh starts, tcl variables like argv, argv will come into picture.

Vishwadeep Singh
  • 1,043
  • 1
  • 13
  • 38
0

The reason for the two line start is to work around a 32 character line limit on the special shebang line.

#!/bin/sh
# A Tcl comment, whose contents don't matter \
exec /some/very/long/path/to/find/a/tcl/executable/path/tclsh "$0" "$@" 
Shawn
  • 1
0

The main historical reason is, as @Shawn mentioned, that some old Unices couldn't handle a long shebang.

The second reason is portability in terms of system layout. Your example #!/usr/sbin/tclsh is an excellent example, because it won't work on my machine. There is no tclsh in /usr/sbin, mine is in ~/.nix-profile/bin/tclsh. It also won't be appropriate for a user that has a tclsh in /usr/sbin but would prefer another version of tclsh that they put earlier in their PATH.

One might think we could just use #!tclsh, which would solve both problems, but there are many systems still today that require an absolute path. But there's an unofficial official portable solution:

#!/usr/bin/env tclsh

This works on your average GNU/Linux distro, on cygwin/msys2 in Windows, and on all kinds of Unices since over two decades back. And it is the shebang that e.g. tklib uses: https://github.com/tcltk/tklib/commit/4ea165c5d8b126185e4bbb341e53289a0efc931d

This trick is so widespread and expected to work, not just for tcl but for other things too, that even NixOS, which otherwise has an empty /usr/bin, has a /usr/bin/env.

For more on /usr/bin/env and interpreters in general, see https://askubuntu.com/questions/88314/what-type-of-path-in-shebang-is-more-preferable .

For more on shebangs and Tcl, see https://wiki.tcl-lang.org/page/exec+magic .

clacke
  • 7,688
  • 6
  • 46
  • 48
0

Super old, but I just stumbled upon this question, and an answer from the man page. You'll see this if you run man tclsh:

An even better approach is to start your script files with the following three lines:

#!/bin/sh
# the next line restarts using tclsh \
exec tclsh "$0" "$@"

This approach has three advantages over the approach in the previous paragraph. First, the location of the tclsh binary does not have to be hard-wired into the script: it can be anywhere in your shell search path. Second, it gets around the 30-character file name limit in the previous approach. Third, this approach will work even if tclsh is itself a shell script (this is done on some systems in order to handle multiple architectures or operating systems: the tclsh script selects one of several binaries to run). The three lines cause both sh and tclsh to process the script, but the exec is only executed by sh. sh processes the script first; it treats the second line as a comment and executes the third line. The exec statement cause the shell to stop processing and instead to start up tclsh to reprocess the entire script. When tclsh starts up, it treats all three lines as comments, since the backslash at the end of the second line causes the third line to be treated as part of the comment on the second line.

undefined
  • 6,208
  • 3
  • 49
  • 59