http://www.perlmonks.org?node_id=532633

japhy has asked for the wisdom of the Perl Monks concerning the following question:

Someone on freenode #perl was asking about hooking STDIN, STDOUT, and STDERR to functions so that when things are read or written to those filehandles, they would be pre-processed. I suggested tie(), and then mentioned that an encoding could probably also do the trick.

So I wrote the following snarky little program:

#!/usr/bin/perl use strict; use warnings; { package MyEnc; use base qw( Encode::Encoding ); __PACKAGE__->Define(qw( japhy )); sub encode { my ($self, $str, $chk) = @_; $_[1] = '' if $chk; $str =~ s/^/> /mg; return $str; } sub decode { my ($self, $str, $chk) = @_; $_[1] = '' if $chk; $str =~ s/^< //mg; return $str; } } open my($in), "<:encoding(japhy)", "input" or die $!; open my($out), ">:encoding(japhy)", "output" or die $!; print $out $_ while <$in>;
If 'input' contains
< abc < def
then 'output' ends up holding
> abc > def
Proof-of-concept. Hooray. But then I tried using STDIN and STDOUT instead:
open my($in), "<&:encoding(japhy)", "STDIN" or die $!; open my($out), ">&:encoding(japhy)", "STDOUT" or die $!;
I entered the same content as is in 'input', but it wasn't decoded, and I got this instead:
< > abc < > def
I tried this, too:
open my($in), "<-:encoding(japhy)" or die $!; open my($out), ">-:encoding(japhy)" or die $!;
That didn't decode OR encode! I had to call binmode() after opening the filehandles to get it to work.

So, what's the deal? Vitals:

Summary of my perl5 (revision 5 version 8 subversion 8) configuration: Platform: osname=linux, osvers=2.6.15, archname=i486-linux-gnu-thread-multi uname='linux ninsei 2.6.15 #1 smp preempt sat jan 7 12:47:52 pst 2 +006 i686 gnulinux ' config_args='-Dusethreads -Duselargefiles -Dccflags=-DDEBIAN -Dccc +dlflags=-fPIC -Darchname=i486- linux-gnu -Dprefix=/usr -Dprivlib=/usr/share/perl/5.8 -Darchlib=/usr/l +ib/perl/5.8 -Dvendorprefix=/us r -Dvendorlib=/usr/share/perl5 -Dvendorarch=/usr/lib/perl5 -Dsiteprefi +x=/usr/local -Dsitelib=/usr/lo cal/share/perl/5.8.8 -Dsitearch=/usr/local/lib/perl/5.8.8 -Dman1dir=/u +sr/share/man/man1 -Dman3dir=/u sr/share/man/man3 -Dsiteman1dir=/usr/local/man/man1 -Dsiteman3dir=/usr +/local/man/man3 -Dman1ext=1 -D man3ext=3perl -Dpager=/usr/bin/sensible-pager -Uafs -Ud_csh -Uusesfio +-Uusenm -Duseshrplib -Dlibperl= libperl.so.5.8.8 -Dd_dosuid -des' hint=recommended, useposix=true, d_sigaction=define usethreads=define use5005threads=undef useithreads=define usemulti +plicity=define useperlio=define d_sfio=undef uselargefiles=define usesocks=undef use64bitint=undef use64bitall=undef uselongdouble=undef usemymalloc=n, bincompat5005=undef Compiler: cc='cc', ccflags ='-D_REENTRANT -D_GNU_SOURCE -DTHREADS_HAVE_PIDS +-DDEBIAN -fno-strict-aliasing -pipe -Wdeclaration-after-statement -I/usr/local/include -D_LARGEFILE_ +SOURCE -D_FILE_OFFSET_BITS=64', optimize='-O2', cppflags='-D_REENTRANT -D_GNU_SOURCE -DTHREADS_HAVE_PIDS -DDEBIAN +-fno-strict-aliasing -pipe -Wdeclaration-after-statement -I/usr/local/include' ccversion='', gccversion='4.0.3 20060128 (prerelease) (Debian 4.0. +2-8)', gccosandvers='' intsize=4, longsize=4, ptrsize=4, doublesize=8, byteorder=1234 d_longlong=define, longlongsize=8, d_longdbl=define, longdblsize=1 +2 ivtype='long', ivsize=4, nvtype='double', nvsize=8, Off_t='off_t', + lseeksize=8 alignbytes=4, prototype=define Linker and Libraries: ld='cc', ldflags =' -L/usr/local/lib' libpth=/usr/local/lib /lib /usr/lib libs=-lgdbm -lgdbm_compat -ldb -ldl -lm -lpthread -lc -lcrypt perllibs=-ldl -lm -lpthread -lc -lcrypt libc=/lib/libc-2.3.5.so, so=so, useshrplib=true, libperl=libperl.s +o.5.8.8 gnulibc_version='2.3.5' Dynamic Linking: dlsrc=dl_dlopen.xs, dlext=so, d_dlsymun=undef, ccdlflags='-Wl,-E' cccdlflags='-fPIC', lddlflags='-shared -L/usr/local/lib' Characteristics of this binary (from libperl): Compile-time options: MULTIPLICITY PERL_IMPLICIT_CONTEXT PERL_MALLOC_WRAP THREADS_HAVE_PIDS USE_ITHREAD +S USE_LARGE_FILES USE_PERLIO USE_REENTRANT_API Built under linux Compiled at Feb 9 2006 22:12:59 %ENV: PERL5LIB="/home/japhy/perllib/share/perl/5.8.7" @INC: /home/japhy/perllib/share/perl/5.8.7 /etc/perl /usr/local/lib/perl/5.8.8 /usr/local/share/perl/5.8.8 /usr/lib/perl5 /usr/share/perl5 /usr/lib/perl/5.8 /usr/share/perl/5.8 /usr/local/lib/site_perl /usr/local/lib/perl/5.8.7 /usr/local/share/perl/5.8.7

Jeff japhy Pinyan, P.L., P.M., P.O.D, X.S.: Perl, regex, and perl hacker
How can we ever be the sold short or the cheated, we who for every service have long ago been overpaid? ~~ Meister Eckhart

Replies are listed 'Best First'.
Re: Encodings not latching onto STDIN
by alexz (Beadle) on Feb 24, 2006 at 20:29 UTC

    Not necessarily anything to do with STDIN/STDOUT, this goes through double encoding and no decoding as well:

    open(INPUT, "<input"); open my($in), "<&:encoding(japhy)", "INPUT" or die $!; open my($out), ">&:encoding(japhy)", "STDOUT" or die $!; print $out $_ while <$in>; close(INPUT);

    The output is:

    > < abc
    > < def
    

    Update: Confirmed that it works fine if you call binmode afterwards. It looks like this bug: http://rt.perl.org/rt3/Ticket/Display.html?id=34595
Re: Encodings not latching onto STDIN
by ambrus (Abbot) on Feb 25, 2006 at 16:48 UTC

    In recent perls, you might be able to use PerlIO::via which is a core perl module to write your own perlio layers in perl.

    However, in some situations, the simplest way to filter the input or output might just be to pipe it through another process (which may be perl or anything else). For example, the following code (from a script I wrote previously) compresses the output with gzip:

    open STDOUT, "|gzip -c" or die "cannot open gzip";