19

I am trying to create something in Perl that is basically like the Unix tee command. I'm trying to read each line of STDIN, run a substitution on it, and print it. (And eventually, also print it to a file.) This works if I'm using console input, but if I try to pipe input to the command it doesn't do anything. Here's a simple example:

print "about to loop\n";
while(<STDIN>)
{
  s/2010/2009/;
  print;
}
print "done!\n";

I try to pipe the dir command to it like this:

C:\perltest>dir | mytee.pl
about to loop
done!

Why is it not seeing the piped input? (I'm using Perl 5.10.0 on WinXP, if that is relevant.)

brian d foy
  • 129,424
  • 31
  • 207
  • 592
Jenni
  • 1,668
  • 4
  • 20
  • 28

5 Answers5

25

This is actually a bug in how Windows handles IO redirection. I am looking for the reference right now, but it is that bug that requires you to specify

dir | perl filter.pl

rather than being able to use

dir | filter

See Microsoft KB article STDIN/STDOUT Redirection May Not Work If Started from a File Association:

  1. Start Registry Editor.
  2. Locate and then click the following key in the registry: HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer
  3. On the Edit menu, click Add Value, and then add the following registry value:
    • Value name: InheritConsoleHandles
    • Data type: REG_DWORD
    • Radix: Decimal
    • Value data: 1
  4. Quit Registry Editor.
C:\Temp> cat filter.pl
#!/usr/bin/perl

while ( <> ) {
    print "piped: $_";
}
C:\Temp> dir | filter
piped:  Volume in drive C is MAIN
piped:  Volume Serial Number is XXXX-XXXX
piped:
piped:  Directory of C:\Temp>
piped:
piped: 2010/03/19  03:48 PM              .
piped: 2010/03/19  03:48 PM              ..
piped: 2010/03/19  03:33 PM                32 m.pm
piped: 2010/03/19  03:48 PM                62 filter.pl
Sinan Ünür
  • 116,958
  • 15
  • 196
  • 339
  • 1
    thanks sinan, very useful answer. this makes it so i don't need the icky batch file workaround – Jenni Mar 19 '10 at 22:17
  • 2
    @Jenni The disadvantage, of course, is that you cannot be sure that `InheritConsoleHandles` will have been set on any other computers to which you might deploy your scripts (which is where `pl2bat.bat` comes in). Thank you for accepting my answer. – Sinan Ünür Mar 19 '10 at 22:19
  • I found this windows bug cannot be reproed reliably. The first time i have to use *dir | perl filter.pl*. But the second time, it just works with *dir | filter.pl*. Nasty. – smwikipedia May 29 '13 at 15:16
13

Try:

C:\perltest>dir | perl mytee.pl
codaddict
  • 445,704
  • 82
  • 492
  • 529
  • hmm... that works, but why do I have to specify it like that? – Jenni Mar 19 '10 at 19:35
  • 3
    Because you are on windows. On unix, you can put #!/usr/bin/perl at the top of the perl file to tell the shell were perl is located. On windows only .exe and .com files can be executed directly. – Byron Whitlock Mar 19 '10 at 19:37
  • ok, in my case "c:\scripts" is in %PATH%, and ".PL" is in %PATHEXT%. i want to be able to call `foo | mytee` from anywhere, without having to remember that in this one case i need to put a "perl" in front of it and the ".pl" at the end. any suggestions or am i out of luck? – Jenni Mar 19 '10 at 19:44
  • 2
    @Jenni You can use `pl2bat` to convert your Perl script into a batch file, which Windows will then be happy to pipe through. – ephemient Mar 19 '10 at 19:52
  • 1
    well i found a workaround. I create a "mytee.bat" file in c:\scripts. that bat file has `@echo off` on first line, and `perl c:\scripts\mytee.pl %1 %2` as the second line. this works--the input gets piped through the bat file to the perl script. Since .bat is before .pl in my PATHEXT makes it so that "mytee" executes "mytee.bat" rather than the perl script. – Jenni Mar 19 '10 at 19:52
6

Could it be Microsoft KB #321788?

Scripts that contain standard input (STDIN) and standard output (STDOUT) may not work correctly if you start the program from a command prompt and you use a file association to start the script.

zoul
  • 102,279
  • 44
  • 260
  • 354
  • sounds like that is the problem. i've found a workaround involving piping through a bat file then calling the perl executable (see accepted answer). thanks! – Jenni Mar 19 '10 at 19:54
  • +1 @zoul I was so busy looking up the information, I did not notice you had already posted the link. – Sinan Ünür Mar 19 '10 at 20:07
1

There's nothing wrong with trying to learn by doing, but a quick search of CPAN shows a number of possible solutions for the tee in Perl problem.

For example: PerlIO::Tee.

Telemachus
  • 19,459
  • 7
  • 57
  • 79
0

Well IMHO, perl is poor substitute for sed ;)

dir | sed s/2009/2010/

Byron Whitlock
  • 52,691
  • 28
  • 123
  • 168
  • 1
    i simplified the part of my script that is irrelevant to my question. – Jenni Mar 19 '10 at 19:36
  • 12
    But perl is a good substitute for: `'sed' is not recognized as an internal or external command, operable program or batch file` ;-) – mob Mar 19 '10 at 19:53