Beefy Boxes and Bandwidth Generously Provided by pair Networks
Problems? Is your data what you think it is?
 
PerlMonks  

Unsuccessful stat on file test

by bluethundr (Pilgrim)
on Mar 02, 2008 at 18:23 UTC ( [id://671517]=perlquestion: print w/replies, xml ) Need Help??

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

Hey Monks, I am just attemmpting to learn to filestat from the llama. I am working off of Chapter 11 in said book, and excercise 1 which states:

Make a program that takes a list of files named on the command line and reports for each one whether its readable, writable, executable, or doesn't exist. (Hint: It may be helpful to have a function that will do all of the file tests for one file at a time.) What does it report about a file that has been chmod-ed to 0? (That is if you are on a Unix system use the command chmod 0 some_file to mark that file as being neither readable, writable nor executable.) In most shells, use a star as the argument to mean all of the normal files in the current directory. That is you should type something like ./ex11-1 to ask the program for the attributes of many files at once.

Iam going to add the rest of the tests once I have it figured out what I'm doing wrong. But this is very preliminary and I don't know why the first test I added is not working.

#!/usr/bin/perl use warnings; use strict; sub filetest { my @answer; return "File does not exist" unless -e $_; } while (<>) { my @answer = &filetest($_); }


This is the message I get when I try to run the program: Unsuccessful stat on filename containing newline at ./ex1-11 line 8, <> line 1

Also, in the subroutine 'filetest' I have an (as yet) unused array called @answer. Once I have the file tests worked out I intend to fill that array with strings indicating the states of the file tests. Once I have all the data collected I plan to return that array.

Thanks for any help you'd care to dispense!

Replies are listed 'Best First'.
Re: Unsuccessful stat on file test
by Corion (Patriarch) on Mar 02, 2008 at 18:31 UTC

    The warning

    Unsuccessful stat on filename containing newline at ./ex1-11 line 8, < +> line 1

    means that you have a filename containing a newline. Which is perfectly explainable, because

    while (<>) { ...

    sets $_ to the entered line including the newline. Use the following to remove the newline and other whitespace from the end of the entered line:

    s/\s*$//;

    Alternatively, for a more fragile solution, look at the chomp function.

      Thanks for the suggestion! I didn't even see the '\n' with my beginner's eyes! But now, my code reads like this:
      #!/usr/bin/perl use warnings; use strict; sub filetest { my @answer; return "File does not exist\n"; unless -e $_; push @answer, "readable " if -r $_; push @answer, "writable " if -w $_; push @answer, "executable " if -x $_; return @answer; } while (<>) { s/\s*$//; my @answer = &filetest($_); print @answer; }


      And now, no matter what file I feed the program, it reports "File does not exist". Also, if I remove the line
      return "File does not exist\n"; unless -e $_;
      the program executes quietly, not printing out any messages. I'm a little confused and befuddled by this.

        After fixing your typo:

        return "File does not exist\n"; unless -e $_;

        needs to become

        return "File does not exist\n" unless -e $_;

        the program works for me:

        C:\Projekte>perl -w tmp.pl tmp.pl readable writable

        So, maybe tell us more exactly what you're typing in, what files exist, and maybe consider adding some more debugging output to your file.

        Your form of calling the filetest function is bad/misleading. filetest is not using any parameters passed to it, it uses the global $_. So you should either call it as filetest(); or make your subroutine actually use its argument.

        As a style note, I never use the ampersand notation for function calls (&filetest()) - I find it too dangerous, because when I leave off the parentheses (&filetest;), that has the unintended side effect of calling filetest with the current values of @_ instead of passing no parameters at all.

        Your code is almost there, but you have to remove the semicolon:
        my @answer; return "File does not exist\n"; # <- extra semicolon! unless -e $_;

        The next problem is:

        while (<>) {
        If your program is named filetest.pl and you call it like:
        ./filetest.pl *.txt
        that loop is actually going to read each line of all the *.txt files in your current directory, and unless each line actually happens to be the name of a file in that directory, your program will correctly tell you that the file does not exists!

        Try adding some extra output (what is $_ ?)

        cheers

        while (<>) {
            # some code
        }

        Is short for:

        while ( @ARGV ) {
            $ARGV = shift @ARGV;
            open ARGV;
            while ( defined( $_ = <ARGV> ) ) {
                # some code
            }
        }

        So you are reading from the files listed on the command line and using their contents as the file names to stat.    Perhaps you meant to do something like this instead:

        for ( @ARGV ) { my @answer = filetest( $_ ); print @answer; }
      You should use s/\s+$//; instead of s/\s*$//;.    s/\s*$//; will modify every string that it operates on (every string has zero whitespace in it) while s/\s+$//; will only modify strings that actually have whitespace at the end.

        Except that, as was the original problem, every string does have whitespace at the end: the EOL character.

        Further, I'm not sure that even if some strings didn't have the EOL whitespace, such as the general case of using s/\s*$// to eliminate whitespace from the end of a string, that this is anything more than premature optimisation. For all I know, perl could already optimise that away, or it may be of no consequence even if it was still "performed". Do you have any evidence that your suggestion improves the OP's code?

        Now, if you were to come in and say to use + instead of * because it more literally descriped what the OP was doing, I'd be in much more (but not complete) agreement. That is, the OP wants to replace all whitespaces leading up to the end of the line, qr/\s+$/, with nothing, whereas the code that is currently there could be read as saying that zero-length strings should be replaced, too, which seems silly. It is apparent to anyone with reasonable amounts of regexp-fu that we don't really care about deleting zero-length strings, so it just seems silly to try replacing them. (Conversely, since it's a "don't care" operation, fixing the code now that it's written and actually works doesn't need doing, either.)

        I can even think of a pathological case where \s* is better than \s+ - and it's not optimisation (well, not of CPU cycles anyway).

        my @filters = ( \&eliminate_ws, \&do_something, \&do_something_else, \ +&etc ); # ... while (<$myfile>) { FILTERS: for my $filter (@filters) { # keep filtering until a filter says to stop. last FILTERS unless $filter->() } } sub eliminate_ws { s/\s*$// } # always returns true. # as opposed to: # sub eliminate_ws { s/\s+$//; 1 } # always returns true, even if s "f +ailed"
        It saves the developer a few keystrokes... but, like I said, it's pathological. ;-)

        Update: given the test below, I'm even more convinced this is premature optimisation. You're doing 1000 tests per sub call, so asking perl what the savings is comes out as (approximately):

        $ perl -e 'printf "%e\n",((1/334000)-(1/1211000))' 2.168248e-06
        so we're saving approximately 2 microseconds using the + instead of *. Seriously, that's not significant unless you really are running 1000's (or 100's of 1000's) of times. That's pretty much the definition of premature optimisation.

      Just curious Corion...what makes chomp more fragile?

        In my experience, any whitespace at the end of what should be a filename is extraneous.

        Often, files which list filenames come with Windows line endings (\x0D\x0A) and on Unix, chomp will only remove the \x0A, leaving remaining but erroneous whitespace.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://671517]
Approved by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others exploiting the Monastery: (2)
As of 2024-04-19 19:28 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found