opening file vs. opening pipeline on NT.
Quote:
>I have found an inconsistency in Tcl7.6 under MS Windows NT 3.51, as
>demonstrated by the following code segment. The code segment shows
>that backslashes in a file name are treated differently depending upon
>whether the file is being opened as a regular file or as a command
>pipeline. When opened as a regular file name, the backslashes are
>preserved. When opened as a command pipe line, blackslash substitution
>appears to be (re)occuring, thereby distorting the original file name.
>set file "c:\\users\\donald\\hello.exe"
>puts "Opening file ..."
>if [catch {set fp [open $file r]} msg] { ;# This is okay.
> puts "Cannot open file: $msg"
>} else {
> close $fp
>}
>puts "Opening command pipeline ..."
>if [catch {set fp [open |$file r]} msg] { ;# This is not okay.
> puts "Cannot open pipe: $msg"
>} else {
> close $fp
>}
>===
>The first open succeeds, but the second one generates the message:
> "couldn't execute "c:usersdonaldhello.exe": no such file or directory"
----------------------------------------------------------
Quick answer: The strictly proper way to open a command pipeline to a
program with arguments is:
open |[list myprogram myarg1 myarg2]
even if there aren't any backslashes in myprogram or myargs. Even if there
are no arguments, the proper way is still:
open |[list myprogram]
-----------------------------------------------------------
Longer explanation:
The problem lies in the dual behavior of the "open" command.
If the first character of the first argument to "open" is a "|", then the
remaining characters in the first argument must make up a proper Tcl list
that describes the program and any arguments to that program.
The above "double-substitution" problem seems to arise only because open
treats its first argument as a list, and it seems so easy to assume that a
string is equivalent to a list of 1 element.
However, given the following:
set f "c:\\name"
The variable f is the string whose bytes are: c : \ n a m e
The variable f is also a 1 element list whose first element is the
string whose bytes are c : \n a m e
^ ^ ^^ ^ ^ ^
You can try this yourself:
% lindex $f 0
c:
ame
Incidentally, to get the 1 element list whose first element is the string
whose bytes are c : \ n a m e
% list $f
{c:\name}
The first-level backslashes were substituted out when assigning the
value to your variable. The argument to open was treated as a list and its
elements (all one of them) were extracted out. Tcl graciously forgave
your mistake of providing the unknown backslash escape sequences \u, \d,
and \h and treated them as the literal characters u, d, and h.
This problem doesn't happen with exec because exec doesn't take a list.
It takes separate arguments that specify the name of the external program and
any arguments to that program:
exec myprogram myarg1 myarg2
Because open takes a list, you must pass it a list. That's the answer.
I thought Tcl was broken when I first read the problem, but Tcl is just being
consistent.
If this still doesn't quite make sense, try playing with lists with more than
one element, which makes it clearer why this is happening. For example:
% set f "puts hi\\nthere"
puts hi\nthere
% lindex $f 0
puts
% lindex $f 1
hi
there
% eval $f
hi
there
vs.
% set f "puts hi\nthere"
puts hi
there
% eval $f
hi
invalid command name "there"