4

Problem

When running exec in perl, I am am unable to redirect the output to a file.

I am running vlc in the exec but since i doubt everyone has it set up I have replaced with echo below, it shows same behaviour for the example.

I am only interested in exec 'command','args' format of exec not the one that spawns a shell since it spawns a subshell with vlc which still prints to screen + other problems with killing it cleanly.


Code

use strict;
use warnings;

my $pid = fork;
if (!defined $pid) {
    die "Cannot fork: $!";
}
elsif ($pid == 0) {
    exec "/usr/bin/echo","done";
}

Tried

exec "/usr/bin/echo","done",">/dev/null";

As expected just prints ">/dev/null", but was worth a try.

exec "/usr/bin/echo done >/dev/null";

Runs sh which then runs echo, works here, but not in my actual problem with vlc, thought i would include anyway since someone will surely suggest it.

Question

How do I redirect output from this exec when using 'command','args' to a file?

Extra

Any more info needed please ask.

Community
  • 1
  • 1
123
  • 10,778
  • 2
  • 22
  • 45
  • try `exec '/usr/bin/echo', 'done';` – Gerhard Sep 27 '17 at 08:51
  • are you just trying to send the output of the exectuted command to a file? – Gerhard Sep 27 '17 at 08:55
  • I tried using Capture::Tiny::Extended, but since it `exec` never returns that doesn't work. – simbabque Sep 27 '17 at 09:01
  • 3
    I *think* the `>/dev/null` is part of the shell. If you run `exec '/usr/bin/echo', 'done'` no shell is involved, which is the idea of the argument list. And without a shell, there is no way to redirect. You need to find a different way to change the handles that the program gets. But I don't know how unfortunately. – simbabque Sep 27 '17 at 09:10
  • Changing the program's STDOUT handle via `open my $fh, '>', 'foo.out' or die $!; select $fh; exec "/bin/echo", 'foobar';` does not work either. – simbabque Sep 27 '17 at 09:14
  • Relevant: https://stackoverflow.com/q/13784269/1331451 and https://stackoverflow.com/q/14543443/1331451. The latter is way more interesting, but it's C only. Not sure how to map that to Perl. – simbabque Sep 27 '17 at 09:15
  • I think the part that actually does the underlying execvp is [here in the source](https://github.com/Perl/perl5/blob/269a3b2e76d9e7b398d9faa62b30dc6d93dd9476/doio.c#L1918). – simbabque Sep 27 '17 at 09:23
  • @simbabque Thanks for the link, struggling to make sense of it though, what is the fd it is using? Does it just inherit whatever perl was started with ? – 123 Sep 27 '17 at 09:39
  • I don't know I'm afraid. My C is not good enough to understand it in detail. Maybe you can write an XS module to do this before calling the `exec`. Or maybe you should pick another approach. – simbabque Sep 27 '17 at 09:52
  • 1
    @simbabque Looks like changing STDOUT does work, just not with `select` strangely? – 123 Sep 27 '17 at 11:10
  • 1
    It makes sense actually. `select` does not change STDOUT. It changes the handle that Perl uses to write to by default. I didn't think of that. Well done. :) – simbabque Sep 27 '17 at 11:12

2 Answers2

4

Turns out you can just change the file descriptors before the exec

use strict;
use warnings;

my $pid = fork;
if (!defined $pid) {
    die "Cannot fork: $!";
}
elsif ($pid == 0) {
    open STDOUT, ">", '/logger/log' or die $!;
    open STDERR, ">", '/logger/log' or die $!;
    exec "/usr/bin/echo","done";
 }
123
  • 10,778
  • 2
  • 22
  • 45
0

I suppose if you just need to print to file, this should work.

easiest possible way to capture the output is by using backticks.

use strict;
use warnings;

open (my $file, '>', 'output.log');
my $pid = fork;
if (!defined $pid) {
    die "Cannot fork: $!";
}
elsif ($pid == 0) {
    print $file `/usr/bin/echo done`;
}
Gerhard
  • 22,678
  • 7
  • 27
  • 43
  • Again, `exec` **does not return**. It's like a fork, it runs and forgets. Your `print` line in the first example will never be reached. In fact, your Perl throws a warning to tell you that. Run this code, and you'll see what I mean: `perl -E 'END{say"end"} exec q{sleep 5 && echo "foo"}; say "bar";'` – simbabque Sep 27 '17 at 09:06
  • @simbabque My mistake. Fixed. – Gerhard Sep 27 '17 at 09:32