Beefy Boxes and Bandwidth Generously Provided by pair Networks
Just another Perl shrine
 
PerlMonks  

redirect output from a command to another command

by Allasso (Scribe)
on Mar 01, 2011 at 21:28 UTC ( #890828=perlquestion: print w/ replies, xml ) Need Help??
Allasso has asked for the wisdom of the Perl Monks concerning the following question:

In bash, you can:
diff <(echo -e "oranges") <(echo -e "apples") 1c1 < oranges --- > apples
Is there a "perl" way to do this - still using diff as an external command, but another way to do the redirection?

Comment on redirect output from a command to another command
Download Code
Re: redirect output from a command to another command
by ikegami (Pope) on Mar 01, 2011 at 21:48 UTC
    I don't know the significance of '63', but this is what bash does on my system:
    use strict; use warnings; use POSIX qw( dup2 ); open(my $src1_fh, '-|', echo => 'apples') or die("open: $!"); open(my $src2_fh, '-|', echo => 'oranges') or die("open: $!"); dup2(fileno($src1_fh), 63) or die("dup2: $!"); dup2(fileno($src2_fh), 64) or die("dup2: $!"); system(diff => '/dev/fd/63', '/dev/fd/64') >= 0 or die("system: $!"); printf("\$?=%04X\n", $?);
    1c1 < apples --- > oranges $?=0100

    On systems without /dev/fd, it reportedly uses temporary files.

    You could also use named pipes.

    Update: What is 64 should be 62.

      thanks ikegami.

      I am wondering why the file descriptor has to be dup'd to 63 - why you can't just use the file descriptor returned from fileno() directly.

      I can see it doesn't work, but I am wondering why.

        First, it doesn't start at 63 and count up, it starts at 63 and counts down. I should have used 63 and 62, not 63 and 64. The reason given by the source is

        Move FD to a number close to the maximum number of file descriptors allowed in the shell process, to avoid the user stepping on it with redirection and causing us extra work.

        There's no mention of anything being special about fd 63 compared to fd 3 (or whatever) other than the user might be using fd 3 already. Perhaps then it's just the close-on-exec flag that varies.

        use strict; use warnings; use Fcntl qw( F_GETFD F_SETFD FD_CLOEXEC ); use POSIX qw( dup2 ); sub keep_open_on_exec { my ($fh) = @_; my $flags = fcntl($fh, F_GETFD, 0); next if $flags & FD_CLOEXEC == 0; fcntl($fh, F_SETFD, $flags & ~FD_CLOEXEC); } open(my $src1_fh, '-|', echo => 'apples') or die("open: $!"); open(my $src2_fh, '-|', echo => 'oranges') or die("open: $!"); keep_open_on_exec($_) for $src1_fh, $src2_fh; system(diff => '/dev/fd/'.fileno($src1_fh), '/dev/fd/'.fileno($src2_fh +)) >= 0 or die("system: $!"); printf("\$?=%04X\n", $?);

        Bingo!

        1c1 < apples --- > oranges $?=0100

        One other note, apparently some systems use /proc/dev/fd instead of /dev/fd.

Re: redirect output from a command to another command
by aquaplanet (Novice) on Mar 01, 2011 at 22:04 UTC

    I don't know if I answer you question, but I post anyway!

    There is the pipe-way:

    open PIPE_ONE, 'echo -e "oranges"|' or die "Can't fork"; open PIPE_TWO, 'echo -e "apples"|' or die "Can't fork (again?)"; my $line_pipe_one = readline(*PIPE_ONE);

    Then these behaves as normal files. Ofcourse if you put the pipe at the beginning is a command you write to:

    open PIPE_THREE, '| wc -l'; print PIPE_THREE "Hello World\n";
      The problems lies in passing the pipes to a child that expects file names.
Re: redirect output from a command to another command
by cdarke (Prior) on Mar 02, 2011 at 08:24 UTC
    <(echo -e "apples")

    This is called process substitution and is also available in modern korn shells. Bash used to use named pipes for this, I'm not sure when it stopped using them, I think when Bash supported co-processes. It looks like named pipes (also known as fifo's) would be a suitable solution here. If you are unfamiliar with named pipes, see the man pages for mkfifo(1).
Re: redirect output from a command to another command
by DrHyde (Prior) on Mar 02, 2011 at 11:22 UTC
    That bashism is pretty flaky. On this 'ere Linux machine ...

    $ diff <(echo -e "oranges") <(echo -e "apples") diff: /dev/fd/63: No such file or directory diff: /dev/fd/62: No such file or directory

    So it appears to make assumptions about the underlying OS which are not always correct. It's a neat trick, but I'd only use it in shell prompt one-liners, not in scripts that might get used elsewhere.

      bummer...

      I wanted to avoid writing stuff to disk, as I will be making large numbers of calls to diff and comparing very short strings.

      It would get pretty expensive to make so many round trips to the hard drive for such a small file.

        I would think launching diff would be relatively expensive.

        The disk cache should eliminate all disk wait for small files.

        Have you considered Algorithm::Diff?

        Allasso:

        If you write to the file, and then use and delete it shortly thereafter, it may not even get written to the disk at all. It may simply reside in memory buffers. So don't be afraid of short-term temporary files. They can even be handy debugging tools--just comment out the delete, so you can see what the intermediate results were in an operation.

        ...roboticus

        When your only tool is a hammer, all problems look like your thumb.

      what version of bash are you using?
        I have no idea. Whatever came with the OS.
      Your bash appears to be configured (built) incorrectly. It can be configured to use a different path, or not use that system at all.

        Then it was incorrectly configured by the OS vendor.

        But in any case, even if all bash users were to upgrade to the latest and greatest bleeding edge version, and spend hours tweaking obscure configuration options, that solution still wouldn't be portable, as it doesn't work in some other shells.

      Said bashism works for me -- running ubuntu 10.04 LTS, or Debian 5

      STILL... I like IPC::Open2 for things like this.

      --
      Tommy
        open2 does not provide any assistance in passing pipes to a child that expects file names.

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://890828]
Approved by ikegami
Front-paged by ikegami
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others exploiting the Monastery: (15)
As of 2014-07-23 18:26 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    My favorite superfluous repetitious redundant duplicative phrase is:









    Results (149 votes), past polls