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

Good afternoon, PerlMonks!

I have been given the following assignment and would like to learn how to see if a file or directory exists.

"Prompt the user to type in one parameter (note that this script only takes in one parameter and should report warnings/errors if the user input contains zero or multiple parameters), check if the input parameter is a filename or a directory name.

1) If the user input is a file, exit when the file is an empty file, and print the last line of the file.

2) If the user input is a directory, exit when the directory is empty, use a hash table to record the filenames inside this directory and the assess ages of these files (the number of days since the files were lastly assessed), find the oldest file (based on modification time) in the directory, and print the absolute path to this file."

I have spent the last 2 days looking a detailed explanation of how to use File::Find but cannot it. Below is my code and I'd like to replace -e $input sections with File::Find because with the -e file operator, I can only search in my current directory and not my entire system. I look forward to your replies and greatly appreciate your time.

Again, I have spent hours looking to better understand File::Find. My 4th edition 'Programming Perl' book doesn't do the job and all sample codes online are commented well enough. Perhaps someone here could do a better job or propose another solution to my problem. I don't think I can simply use file/directory test operators or globs to do what I want to do because they are not going to scan the entire system.

#! /usr/bin/perl # Assignment 9, check and read files and directories use strict; use warnings; use Module1; assnintro(); my @parameter; my $input; do { $input = (); @parameter = (); pamcheck( \@parameter ); $input = $parameter[0]; if ( -e $input ) { } else { print color 'red'; print "The file or directory you entered, doesn't exist.\n"; print color 'reset'; } } until ( -e $input ); if ( -d $input ) { print "I am a directory.\n"; } else { } exit; sub pamcheck { my ($parameter) = @_; my $elementcount; do { @$parameter = (); print "Enter one file or directory name: "; @_ = split( /\s+/, <> ); push( @$parameter, @_ ); $elementcount = 0; foreach (@_) { $elementcount++; } if ( $elementcount != 1 ) { print color 'red'; print "Please enter only ONE parameter.\n"; print color 'reset'; } else { } } until ( $elementcount eq 1 ); }

Replies are listed 'Best First'.
Re: verifying user input with Find::File
by GrandFather (Saint) on Nov 02, 2014 at 21:37 UTC

    It may help to copy and paste your teacher's actual instructions. Items 1 & 2 aren't altogether clear, especially instructions of the form exit when the file is an empty file, and print the last line of the file - an empty file doesn't have a last line. Have you "reinterpreted" the actual instructions for us?

    You don't show us the code you've tried that uses File::Find.

    Did your teacher specify that you need to search "everywhere" for the file (that's a pretty big ask depending on how you choose to interpret "everywhere"). If not, just assume the current directory and ignore File::Find.

    There are many quirks in your code. Here's a cleaned up version of your code followed by some comments on the changes.

    #! /usr/bin/perl # Assignment 9, check and read files and directories use strict; use warnings; use Module1; assnintro(); while (1) { print "Enter one file or directory name: "; my @params = split /\s+/, <>; if (@params != 1) { print color 'red'; print "One directory or file entry expected.\n"; print color 'reset'; next; } my $input = $params[0]; if (!-e $input) { print color 'red'; print "The file or directory '$input' doesn't exist.\n"; print color 'reset'; } elsif (-d $input) { last if !doDir($input); } else { last if !doFile($input); } } sub doDir { my ($input) = @_; print "'$input' is a directory.\n"; my @subDirs = glob $input; return if !@subDirs; #... return 1; } sub doFile { my ($input) = @_; print "'$input' is a file.\n"; #... return 1; }

    Of note:

    • do {} until loops are unusual in Perl and don't work the same as while and for loops.
    • my $count = @array; assigns the number of elements in @array to $count.
    • $input = (); is at best redundant.
    • Use my @parameter; inside the loop to make it clear that is where it is used.
    • Don't use empty blocks in if statements. Simply omit the else block, or complement the test as appropriate.
    • Don't use the parameter array @_ as a general purpose array. sub pamcheck does some very strange stuff and doesn't work the way you think it does.
    Perl is the programming world's equivalent of English
Re: verifying user input with Find::File
by Laurent_R (Canon) on Nov 02, 2014 at 21:28 UTC
    because with the -e file operator, I can only search in my current directory

    Sure you can look into another directory with the -e and other file test operators:

    $ perl -e '$search_file = "/usr/bin/perldoc"; print $search_file if -e + ($search_file);' /usr/bin/perldoc $ perl -e '$search_file = "/usr/bin/perldoc"; print "file size of $sea +rch_file is ", -s ($search_file), "\n";' file size of /usr/bin/perldoc is 224
    But of course you can't walk through your entire file system with just the -e operator. But from reading your assignment, your don't seem to be required to do that and looking into the current directory for files and one subdirectory should be sufficient.

    Using the File::Find module is certainly a good option in general, but might not be what you are requested to do in an assignment. And it involves relatively advanced topics such as callbacks and subroutine references that are likely to be beyond what you have covered so far.

Re: verifying user input with Find::File
by dissident (Beadle) on Nov 02, 2014 at 21:15 UTC

    I guess you missed that well-written introduction into File::Find written by a Perl Monk. Most of your questions are answered in the code samples there.

    And regarding the "Camel book" you mentioned... it's more like an presentation of Perl than a complete documentation. I like to read it when taking a bath. When at work, I'd suggest to instead read the more detailed Perl documentation.

Re: verifying user input with Find::File
by boftx (Deacon) on Nov 03, 2014 at 04:34 UTC

    No matter what you do, learn about taint mode and how and why to untaint user input, especially since you are going to be using it to look for file names. It is impossible to overemphasize the importance of doing so!

    Trust me, you'll be glad you did when you get out in the real world. :)

    You must always remember that the primary goal is to drain the swamp even when you are hip-deep in alligators.
Re: verifying user input with Find::File
by james28909 (Deacon) on Nov 02, 2014 at 21:39 UTC
    maybe something like:
    use File::Slurp; my @dir_contents = read_dir($argv[0]); for my $entry(@dir_contents){ if (-d $entry){ print $entry is a directory"; } elsif (-f $entry){ print $entry is a file"; } else { print "not a directory or file <.<"; } }
    but i think youd be better off looking at File::Find and maybe File::Basename and maybe even use Cwd as well. that way if you decide to ever take the script further than just testing for if file or dir, then you will have everything set and ready to go.