WAD Multiple TEE causese weird variable scoping?

Dec 13, 2011
3
0
#1
Usually when I find something weird in TCC/4NT, it's some kind of CMD compatibility. But, I can't see how this could be intentional! I have repro'd in both TCC LE 13.00. 09 and 11.00.40. We are long-time 4NT users here at our shop.

I have made a very simple repro case for this. The assignment to EXAMPLE_VAR inside the block seems to have no effect when multiple TEEs are used on the echo statement.

As you might imagine, this code originally was meaningful and wasn't TEEing to the NUL device!

------------------------------------------------------
Batch file:

@echo off
set EXAMPLE_VAR=0
(
echo This line uses TEE | tee /a nul | tee nul
set EXAMPLE_VAR=1
)
echo After block: EXAMPLE_VAR = %EXAMPLE_VAR

---------------------------------------------------------
Output:

This line uses TEE
After block: EXAMPLE_VAR = 0
 

Charles Dye

Super Moderator
Staff member
May 20, 2008
3,599
46
Albuquerque, NM
prospero.unm.edu
#2
I think what's happening is that your second SET is being executed in one or the other of the right-hand shells. You can use more parentheses to clarify your intentions for the parser:

Code:
@echo off
set EXAMPLE_VAR=0
(
   ( echo This line uses TEE | tee /a nul | tee nul )
set EXAMPLE_VAR=1
)
echo After block: EXAMPLE_VAR = %EXAMPLE_VAR
Or, if you don't actually need the command group, get rid of all the parentheses.
 

samintz

Scott Mintz
May 20, 2008
1,270
11
Solon, OH, USA
#3
The issue is the command group as near as I can tell.
(
command1
command2
)

is essentially: command1 & command2

So, looking at your example, it would appear that the group is causing the set to execute inside one of the piped processes. That keeps the original value intact.

-Scott
 
Dec 13, 2011
3
0
#4
Thank you for your very quick replies on this one, gentlemen. This test case is very contrived; originally this was an IF group inside a FOR group.

Scott, I like the direction you're going with this. But, why does this new version work properly? The only change is to remove the second TEE. I would expect this version to exhibit the same behavior.

@echo off
set EXAMPLE_VAR=0
(
echo This line uses TEE | tee /a nul
set EXAMPLE_VAR=1
)
echo After block: EXAMPLE_VAR = %EXAMPLE_VAR
 

rconn

Administrator
Staff member
May 14, 2008
10,550
97
#6
Scott, I like the direction you're going with this. But, why does this new version work properly? The only change is to remove the second TEE. I would expect this version to exhibit the same behavior.
TCC goes through some gymnastics to move the compound argument following a pipe to execute in the parent shell instead of the child pipe shell. (Strictly speaking this isn't the technically correct way to parse the line, but 99.999% of the time the user doesn't realize that piped processes execute in a different shell and can't modify the parent environment.)

That is impossible to do when you're nesting pipes -- TCC has no idea whether you want to execute the SET in the parent, the child pipe process, or the child of the child pipe process. (The latter is actually what you asked it to do.) You'll have to use command grouping to force the SET to the proper shell.
 
Dec 13, 2011
3
0
#7
Thanks Rex, and everyone. This has been very educational. I really had no idea that so much magic was going on behind the scenes with multiple pipes, or groups! I certainly have learned something today.

Sometimes, the very easy syntax of groups can lead to surprising results. Groups are not just "inline gosubs". It is easy to think of them that way because they are both EOL-delimited. But that is a very sloppy way of thinking about groups, as Scott illustrated by rephrasing my group as a single-liner with command-separators. *That* is how we should be thinking when we use multiline groups!

Let me add one final example so that we can all see how surprising this can be. Though, this is really not very surprising considering what we've learned today.
--------------------------------------------------
@echo off
set EXAMPLE_VAR=0
(
echo This line uses multiple pipes | tee /a nul | tee nul
set EXAMPLE_VAR=1
echo Inside block: EXAMPLE_VAR = %EXAMPLE_VAR
)
echo After block: EXAMPLE_VAR = %EXAMPLE_VAR

---------------------------------------------------

This line uses multiple pipes
Inside block: EXAMPLE_VAR = 1
After block: EXAMPLE_VAR = 0
----------------------------------------------------
Not only does the SET occur in a different shell, but also the subsequent echo! After the use of multiple pipes, the rest of this group has been relocated to a subprocess. That is pretty neato.

Thank you all so much! I am going to take much greater care with my groups.