0

I have a tcl/tk script that I run through wish. However, I noticed certain command line arguments are passed to wish instead of my script. For example, if I type ./script -h I get the wish help output instead of my scripts help output.

The following code demonstrates this, where puts $arvg should show the command line arguments. If I use arguments that are not used by wish, like "-i", then they are correctly passed to my script and printed out.

#!/usr/bin/env/ wish

puts $argv

How can I ensure the command line arguments get passed to my script instead of the interpreter?

1
  • I deleted my answer. The -- argument in wish doesn't mean "this is the last non-option argument", but "this is the last argument with any meaning to wish". Oops! Commented Oct 2, 2022 at 0:53

2 Answers 2

2

Instead of invoking wish in the shebang (directly or through env), invoke tclsh. That way you can manipulate the $argv that Tk will see:

#!/usr/bin/env tclsh

# Copy the command line arguments to a new variable
set arglist $argv

# Turn tclsh into wish without passing the command line options
set argv {}
package require Tk

puts $arglist

I normally do all my command line processing before the package require Tk. So then it's only necessary to clear argv before starting Tk.

Sign up to request clarification or add additional context in comments.

Comments

1

For well-behaved programs, one possible answer would be this:

#!/usr/bin/env -S interpreter --

The interpreter, whatever it is, would treat the -- as the last option argument. Then it would treat the following argument as the script name, and the remaining arguments as arguments to the script.

Alas, wish doesn't obey this convention. While it supports --, that argument means "this is the last argument that wish itself processes; everything else goes to the script". The argument after -- is not treated a script file to read.

The documentation for -- says that the remaining arguments are passed to the script, but I can't see any way of specifying what that script is, if the -- option is used, other than placing it before the --.

Your best bet might be a shell wrapper:

#!/bin/sh
myname=$0
tkscript="${myname%.sh}.tcl"
exec wish "$tkscript" -- "$@" # Thanks to Donal K. Fellows for exec reminder.

The idea is that you have the above script under the name, say, foo.sh. In the same directory as foo.sh, there is a foo.tcl script: the real one.

The idea is that if the above script is invoked as /path/to/foo.sh, it will calculate the adjacent script's name as /path/to/foo.tcl. That is passed as the argument to wish, then the -- option to say "this is the last argument processed by wish" and then the script's own arguments, which are no longer interpreted by wish even if they look like wish options.

You might not want the .sh suffix on it, but just call it by an unsuffixed name like foo, in which case the tkscript assignment simplifies to:

tkscript="${myname}.tcl"

4 Comments

Use exec wish ... to avoid creating a new process; you are just adjusting the launch options.
@DonalFellows Maybe it's about time the shells put in the tail call elimination themselves; who can keep remembering to exec the last line?
The patch I originally proposed for GNU Coreutils env (with working implementation, documentation and test cases) would handle this situation; I supported the {} notation to indicate where in the arguments the script name is to be inserted.
The shells assume you write what you mean. By default, shells do post-processing of the result of your calls (to do little things like detect failures, information that you happen to throw away in this case) so they can't just assume that you meant to delegate execution entirely. If you want that, you need to write exec to say it. That's the rule.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.