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

I've been working in Learning Perl and recently got to the section on opening file handles to system commands and piping to them. I tried:
open(COPY, "| cp") || die("Could not open COPY: $!");; print COPY "file1 file2" || die("Could not print to COPY: $!"); close COPY || die("Could not close COPY: $!");
This failed on close, and I learned from reading perlman:perlipc that if a command can't accept input from a pipe, it will always die on close.

My question is: is there any way to tell ahead of time whether or not the command I want to pipe to will accept my input, a more efficient test than just seeing if it dies on close? Is there a list compiled somewhere of standard Unix commands that will/won't take input from a pipe?

Replies are listed 'Best First'.
Re: Piping into system commands
by Fastolfe (Vicar) on Jan 24, 2001 at 07:32 UTC
    Firstly, 'cp' takes its arguments on the command line. It does nothing with data sent over STDIN. You probably mean to do something like this:
    system("cp", "file1", "file2") and die "..."; # system returns false +upon success, not true
    Of course, it's only a few lines to repeat this procedure in Perl if you wanted to avoid the system overhead entirely.

    Additionally, this statement is incorrect:

    print COPY "file1 file2" || die("Could not print to COPY: $!");
    The || operator binds very tightly here (it has a high precedence), so if we were to use parenthesis just about everywhere we could, it would look a bit like this:
    print COPY ("file1 file2" || die("..."));
    When making 'or'-type decisions based upon execution, you want to use a lower-precedence operator like 'or' instead of '||'. Only use || when you want to make a decision based upon values, and want to use $value1 || $value2.
Re: Piping into system commands
by lemming (Priest) on Jan 24, 2001 at 07:24 UTC

    Any command that takes STDIN will work for piping into. cp is not one of those. I would read the man page for any command that you'll want to call directly from a Perl program. There are slight differences between platforms that can cause problems if you depend on them. You shouldn't be calling programs that you don't know the behaviour of.

    One of the Perl ways of doing this without decent error checking. It works, but I see some changes need to be made before I'd allow it on my system.
    use strict; use warnings; use File::Copy; my $file1 = shift || die "No args!\n"; my $file2 = shift || die "Second file please?\n"; copy("$file1","$file2");
    I still recommend more manual reading
Re: Piping into system commands
by chipmunk (Parson) on Jan 24, 2001 at 07:39 UTC
    In general, a Unix command will accept arguments such as filenames on the command line, and (where applicable) will accept arbitrary input via STDIN.

    Commands that accept arbitrary input are ones that do some sort of processing on the input, like sorting it, or searching it, or reformatting it. The cp command just copies files; it doesn't do any processing on arbitrary input.

    For example, the sort command: you can sort file, to sort the contents of file, or who | sort to pipe the output from who as input to sort. On the other hand, if you do echo 'file' | sort, then you'll just get 'file' back again, rather than the contents of file in sorted order.

    So, a good rule of thumb is; if you're telling a command what files to process, you should specify the files as command line arguments. (e.g. system "cp file1 file2";) For arbitrary input, open a pipe. Whenever you're not sure, check the manpage for the command.

    BTW, you might prefer to use the File::Copy module for copying files in Perl.

(tye)Re: Piping into system commands
by tye (Sage) on Jan 24, 2001 at 07:20 UTC

    What you have done is the equivalent of:

    echo file1 file2 | cp
    Does that help?

            - tye (but my friends call me "Tye")