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

Okay, haven't done much with perl in a couple years... so forgive me if I don't have much knowledge to work with...

I'm trying to put together a program that will sit with a STDIN prompt asking for a number, and when you enter the number, it will do a directory listing of /usr/local/diskFarm/*/*/ and find the file that has that number in it, and then return the entire path to file.

Some things to know, our filesystem is
/usr/local/farm/company/subcompany/
/usr/local/farm/company/subcompany/log/
/usr/local/farm/company/subcompany/input/
/usr/local/farm/company/subcompany/input/save/
/usr/local/farm/company/subcompany/input/failed/

The number is assigned to each division of the subcompany and is part of the filename for the input file and the log file. The end goal is to enter the subcompany number and have it give me the location of any files with that number, and then tail the last 10 lines of the log... I've got it to the point where it will find the file if it's in input... but it won't tell me where it found it.
#!/usr/bin/perl # # This is a program that is intended to allow FEP operators # a tool that will facilitate in monitoring incoming transmissions # and preprocessor logs. # #------------------- Define Subroutines ------------------# # lsForCorp Subroutine designed to take an argument and do a directory # listing looking for that argument on the farm and then returning the + # results. sub lsForCorp { local($corp) = $_[0]; @farmLs = `ls -ltr /usr/local/farm/*/*/input/`; foreach $farmLsFile (@farmLs) { if ($farmLsFile =~ /$corp/) { print $farmLsFile; } # end of if statement } # end of foreach statement } #end of lsForCorp subroutine #------------------- End Subroutines ---------------------# print "What corp are you looking for? " ; $whatWeWant = <STDIN>; &lsForCorp("$whatWeWant");
So any ideas on what I need to do differently? I know right now I'm just looking at the input directory, trying to start small, was going to have it check save and failed, and find the log after I figured out the path structure.

Replies are listed 'Best First'.
Re: Directory listing
by Tanktalus (Canon) on Jan 17, 2005 at 04:51 UTC
    @farmLs = `ls -ltr /usr/local/farm/*/*/input/`;

    It's always somewhat disconcerting when I see someone writing shell scripts inside their perl code ... I'm not sure where I first heard it, but the old saying that "you can write ForTran in any language" is just as true about any other seriously limited language (e.g., batch files or shell scripts)...

    Especially when perl has the ability to do this already.

    require File::Spec; @farms = glob( File::Spec->catfile( qw(usr local farms * * input), "*$corp*" ) );
    This gives us a full program like this:
    #!/usr/bin/perl # # This is a program that is intended to allow FEP operators # a tool that will facilitate in monitoring incoming transmissions # and preprocessor logs. # #------------------- Define Subroutines ------------------# # lsForCorp Subroutine designed to take an argument and do a directory # listing looking for that argument on the farm and then returning the + # results. use strict; sub lsForCorp { my $corp = $_[0]; require File::Spec; my $path = File::Spec->catfile( File::Spec->rootdir(), qw(usr local farms * * input), "*$cor +p*" ); my @farms = glob($path); foreach my $farm (@farms) { print $farm, "\n"; } # end of foreach statement } #end of lsForCorp subroutine #------------------- End Subroutines ---------------------# print "What corp are you looking for? " ; chomp(my $whatWeWant = <STDIN>); lsForCorp($whatWeWant);
    Note a number of changes - no more local, using strict, a few minor changes to variables, and especially using my all over the place, getting rid of the & in front of calling a function, chomping your input (that was likely your original problem there), using the glob as above - which eliminates the if statement in your loop. My not-so-humble opinion that this is not only correct (I tested it here), but better perl code ;-) Hope it helps.

      Awesome, that is a good start... now if I get rid of the input and just have
      qw(usr local farms * *), "*$corp*"
      Will it do a recursive search through log, input, save, failed, etc? or will I have to make a seperate subroutine for each of those paths?

      Also, would you be willing to do a small breakdown of what each line is actually doing (only the one's you changed, I should hope I know what the ones I wrote were doing) so I'm not just taking prefabbed code... I'd like to relearn this (I never really got far with perl in the first place, so I guess learn would be a better word than relearn)

      Also, with glob, is there some way that I can get the date, time, and bytecount of the files being returned?

        Here's some comments in-line:

        # supersearch this one - short version: it detects most common coding +errors. # always use it. use strict; sub lsForCorp { my $corp = $_[0]; # File::Spec allows you to create a directory structure in # a platform-independant manner. Not a big deal to you since # this probably only runs on unix anyway, but all my code # has to run on both unix and Windows, so it's just a # habit. require File::Spec; my $path = File::Spec->catfile( # this reconstructs the path - if you print $path, you'll # see exactly the path you were using - again, no big deal # if portability is unimportant (and "/usr/local" is not # really portable either ;->) File::Spec->rootdir(), qw(usr local farms * * input), "*$cor +p*" ); # let glob do all the work rather than ls. Besides, ls # calls the same thing as glob under the covers, more or # less. (Technically, it's the shell.) my @farms = glob($path); foreach my $farm (@farms) { # no if statement since the glob already only gets files that match # *$corp*, which is the glob way of saying /\Q$corp/. print $farm, "\n"; } # end of foreach statement } #end of lsForCorp subroutine #------------------- End Subroutines ---------------------# print "What corp are you looking for? " ; # chomp it to remove the trailing newline. chomp(my $whatWeWant = <STDIN>); # & doesn't do what you think it does - avoid it unless you # actually mean to do what it does. lsForCorp($whatWeWant);

        If you want to search more than just "input", you want to search all directories at that level, you just change "input" to "*", and it'll handle that: /usr/local/farms/*/*/*/*$corp*. As long as everything is at the same depth in the directory tree, this works fine. If you're looking for files at different depths, you'll need to use File::Find or something similar.

        As for getting the date/time/bytecount, you'll need to use the -X functions and/or stat or File::stat. e.g.:

        use File::stat; # ... my $s = stat($farm); printf "%s: Age [%s], size [%d]\n", $farm, $s->mtime(), $s->size();

Re: Directory listing
by Zaxo (Archbishop) on Jan 17, 2005 at 05:18 UTC

    I think you should chomp your input from STDIN, the lineend is probably not part of the file name. Davido's suggestion of File::Find is certainly what you want, but I like glob when a piece of a file name is known, here are a couple of fragments to help.

    use File::Find; sub ask { my $response; print @_; chomp($response = <STDIN>); $response; } my $searchbase = '/usr/local/farm/company/subcompany/'; our @files; our $whatWeWant = ask 'What corp are you looking for? '; find sub { -d and push @files, glob "$File::Find::name/*$whatWeWant*"; }, $searchbase; { local ($\,$,) = ($/, $/); print @files; }
    Untested.

    After Compline,
    Zaxo

Re: Directory listing
by davido (Cardinal) on Jan 17, 2005 at 04:40 UTC

    Since you are planning a recursive directory search, I would use File::Find, and put your number search in the wanted() sub.


    Dave

      I dunno, for some reason I've always had a hard time getting my head around the API for File::Find. For me, and YMMV, File::Find::Rule is a little more intuitive.

      -- vek --
      Can you give my a syntax example? And how about CPU time, will this take longer than doing an ls and string matching? We've got a lot of folders to go through, and I'm looking for a relatively quick way to do it...

        "ls /usr/local/farms/*/*/input/*$corp*" is not recursive. File::Find isn't incredibly slow if there aren't any subdirectories under /usr/local/farms that aren't matched by /usr/local/farms/*/*/input, but glob is likely to be faster than File::Find.