jktstance has asked for the wisdom of the Perl Monks concerning the following question:
Hi again,
I have this code from a script I want to modify:
open FP, "find . -ls |"
From what I understand, this will pipe the output of find() to the FP filehandle. Then you can loop through it like a standard text file:
while (my $file = <FP>) {
chomp $file;
...
}
What is the advantage of using open FP, "find . -ls |" and then looping through the handle, as opposed to using backticks?
my @files = `find . -ls`;
foreach my $line (@lines) {
chomp $file;
...
}
Also, regarding system(), it returns the exact same thing as using backticks, plus the final element of @files is the exit status (0 in this case for success). I thought system() was only supposed to return an exit status, not full output
TL;DR -- why did the coder use a pipe in open() instead of using backticks or system()?
FYI, this script basically reads the find . -ls output and writes a file listing files and their sizes.
Re: Interacting with the shell ( backticks, system(), open -| )
by BrowserUk (Patriarch) on Feb 04, 2014 at 22:33 UTC
|
What is the advantage of using open FP, "find . -ls |" and then looping through the handle, as opposed to using backticks?
- If the output is extensive, reading and processing it a line at a time rather than slurping it all into memory then processing it, may avoid using a large amount of memory.
- If the output takes a while to produce, your program can be processing the first lines whilst the other process is still producing the rest.
- Maybe you don't need all the output.
Once you found what you are looking for, you can discard the rest.
If you capture the pid returned by the piped open, you can even kill the external process before it completes.
regarding system(), it returns the exact same thing as using backticks
What makes you think this? system does not capture or return any output from the program.
why did the coder use a pipe in open() instead of using backticks ...?
Perhaps he was dealing with very large directories.
With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
"Science is about questioning the status quo. Questioning authority".
In the absence of evidence, opinion is indistinguishable from prejudice.
| [reply] |
|
| [reply] [d/l] |
|
So piping the command to a filehandle should keep the find . -ls process open until <FP> is closed?
The external process is connected to your end of the pipe via one or two buffers, usually 4k or 8k each.
When (if) those buffers fill up, the external process will be blocked when trying to add more output to the pipe and thus will have to wait until your program reads some of the data from your end of the pipe before continuing.
If the external program produces less output than the combined size of the buffers, it will run to completion immediately and the output will wait in the pipe until your program reads it.
With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
"Science is about questioning the status quo. Questioning authority".
In the absence of evidence, opinion is indistinguishable from prejudice.
| [reply] |
Re: Interacting with the shell ( backticks, system(), open -| )
by davido (Cardinal) on Feb 04, 2014 at 22:32 UTC
|
I don't see your code where a call to system produces the same output as backticks. Can you provide an example that we can execute ourselves to see what you mean?
The equivalence of `find . -ls`; and open FP, 'find . -ls |' is kind of where the functional overlap ends. Backticks are more confining than using open, and while you can emulate what backticks do using open, you cannot emulate everything that open can do using backticks. And with backticks you get everything back at the same time. In the simple example you presented, one of the best reasons to go with the open version is to allow you to work on the output one line at a time, without storing it all in an array. And the advantage of the backticks in this same example is just the simplicity and brevity.
| [reply] [d/l] [select] |
|
Dave,
Here is the script I use when using system():
#! /usr/bin/perl
use strict;
use warnings;
my @files = system "find . -ls";
foreach my $file (@files) {
chomp $file;
print "$file\n";
}
And it outputs the following (I deleted the userid column):
1105134059 2 drwxr-xr-x 3 .... users 2048 Feb 5 09:40 .
1105135378 1 -rw-r--r-- 1 .... users 506 Feb 3 10:54 ./t
+est.pl
1105135090 1 -rw-r--r-- 1 .... users 93 Jan 24 15:41 ./c
+alendar.pl
0
Notice the trailing 0? If I replace my @files = system "find . -ls"; with my @files = `find . -ls`;, I get the same output, except the trailing 0 is missing.
I understand why system() prints out the trailing 0 (it's the exit status of find()), but why is it also outputting everything find() outputs? I thought it should only return the exit status.
| [reply] [d/l] [select] |
|
find itself outputs the lines. System does not return them, it only returns the exit status. You can verify none of the lines is printed via print by wrapping the values in parentheses:
print "($file)\n";
The zero will be the only parenthesized string.
| [reply] [d/l] [select] |
|
| [reply] [d/l] |
Re: Interacting with the shell ( backticks, system(), open -| )
by Discipulus (Canon) on Feb 05, 2014 at 08:49 UTC
|
| [reply] [d/l] |
|
| [reply] |
Re: Interacting with the shell ( backticks, system(), open -| )
by afoken (Chancellor) on Feb 05, 2014 at 19:10 UTC
|
why did the coder use a pipe in open() instead of using backticks or system()?
An even better question: Why didn't (s)he use File::Find? There is no need to spawn a child process just to find files in a directory tree.
Consider rewriting that code to use File::Find.
Alexander
--
Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)
| [reply] [d/l] |
|
How could I use File::Find and still keep the feature of the pipe into a filehandle, namely the while loop only reading in one line at a time? As with using backticks, the only way I can see is to read the entire output of File::Find into an array, which could be very large.
| [reply] |
|
How could I use File::Find and still keep the feature of the pipe into a filehandle, namely the while loop only reading in one line at a time? As with using backticks, the only way I can see is to read the entire output of File::Find into an array, which could be very large.
Do you know File::Find? If not, did you read the documentation?
My guess is that your answers are NO and NO, and the second one makes me sad.
File::Find has no output. File::Find uses a callback function (commonly called wanted) that is invoked exactly once for every file found. The loop that traverses the directory tree, the equivalent of the pipe open and the while / readline loop, is inside the File::Find::find funcion, so you don't need the while loop any more. The equivalent of the loop body is the callback function, this is where you process any file found by File::Find. You do not need an array.
Consult the really good documentation of File::Find for details.
Alexander
--
Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)
| [reply] [d/l] [select] |
|
|