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

Hi all, I was wondering if you could find the reason for this issue in the code posted below?

I am trying to automate the way I use a program (Gromacs) and edit its outputs. So first I loop through my molecules and issue the Gromacs command (g_angle) which works fine. Then I commented the g_angle command out to test the rest of the script. What I want to do now is open (for reading) the output of the previously used command, to manipulate it and write another file.

However the line open $input, '<', '/Users/athina/check-ma/$ID${l}/F${j}\/gangle-dihedrals-${i}${id}${k}.xvg' or die $!; fails as "no such file or directory" but the file exists.

The line print  `ls /Users/athina/check-ma/$ID${l}/F${j}\/gangle-dihedrals-${i}${id}${k}.xvg`; that I've used as a test works fine and returns the file.

What is happening? :( Thank you so much for your time!

#!/usr/bin/perl use warnings; use strict; my $id = "ac"; my $ID = "AC"; my $g_angle = "/usr/local/gromacs/bin/g_angle"; my $i; my $k; my $input; my $output; #disable Gromacs backups because otherwise it fails after writing 99x +copies if(!defined $ENV{GMX_MAXBACKUP}) { $ENV{GMX_MAXBACKUP}=-1 } for ($i=2; $i<=2; $i++) { my $j = sprintf ("%05d", $i); for ($k=15; $k<=15; $k++) { my $l = sprintf ("%04d", $k); my $path = "/Users/athina/check-ma/$ID${l}/F${j}"; my $dashFolder = "/media/data/athina/dash-test/$ID${l}"; #`$g_angle -f $path\/${i}${id}${k}.mdsol.centered.xtc -n /Users/ath +ina/check-ma/$ID${l}/F00001/dihedrals-1${id}${k}.ndx -type dihedral - +all -ov $path\/gangle-dihedrals-${i}${id}${k}.xvg -od $path\/out.xvg` +; print `ls /Users/athina/check-ma/$ID${l}/F${j}\/gangle-dihedrals-${i} +${id}${k}.xvg`; open $input, '<', '/Users/athina/check-ma/$ID${l}/F${j}\/gangle-dih +edrals-${i}${id}${k}.xvg' or die $!; open $output, '>', '/Users/athina/check-ma/$ID${l}/F${j}\/in-dihedr +als-${i}${id}${k}.txt' or die $!; while (my $line = <$input>) { chomp $line; # remove end-of-line character $line =~ s/^\s+//; # strip leading whitespace next unless $line; # skip blank lines # skip first 12 lines next unless $. > 12; # $. contains current line number my @columns = split(/\s+/, $line); # split columns on whitespac +e my $col1 = shift @columns; # column 1 (throw away) my $col2 = shift @columns; # column 2 (throw away) my $col3 = shift @columns; # column 3 (special - keep this one) my $result = sprintf("%8.3f", $col3); # special format for col 3 # loop over remaining columns, appending to result string for my $c (@columns) { my $data = sprintf("%9.3f", $c); $result .= " $data"; } print $output "$result\n"; } ### } ### for k loop } ### for i loop

Replies are listed 'Best First'.
Re: 'open for read' fails as "no such file or directory" but file is there
by Eily (Monsignor) on Nov 02, 2016 at 12:51 UTC

    You can try to see exactly what file perl tries to open and print it in the error message by doing the interpolation in a temporary variable:

    my $file = '/Users/athina/check-ma/$ID${l}/F${j}\/gangle-dihedrals-${i +}${id}${k}.xvg'; open $input, '<', $file or die "Can't open file $file: $!";
    Once this is done, you can replace the single quotes by double quotes, as strings in single quotes are used literally and no interpolation happens (perl doesn't replace the variables by their value).

    The variable with the file name is a good habit to have, so that you can have the exact same string used by open and printed in the error without having to assume that you and perl are looking at the same file.

      This was the message I got when tried the variable with the file name.

      Can't open file /Users/athina/check-ma/$ID${l}/F${j}\/gangle-dihedrals-${i}${id}${k}.xvg: No such file or directory at auto-dash.pl line 33.

      The problem was the single quotes, once I replaced them with double quotes the problem was solved. Phew, that was a super easy one that I didn't even think to do... :( Thank you for your time and advice!

        Here's a suggestion for future development -- instead of creating the filename to be opened just in the open statement, create it ahead of time:

        my $filename = # complicated work that builds filename open ( my $fh, '<', $filename ) or die "Unable to open $filename for read: $!"; # ... close ( $fh );
        You may hear about using autodie instead, but I like writing out the open statement and the die statement, as it gives the debugger me more information from the developer me. In this case, if it fails, you can immediately copy and paste the filename into an ls command and check to see if Perl can't find the file .. or if developer me is an idiot. :)

        Alex / talexb / Toronto

        Thanks PJ. We owe you so much. Groklaw -- RIP -- 2003 to 2013.

Re: 'open for read' fails as "no such file or directory" but file is there
by kcott (Archbishop) on Nov 02, 2016 at 20:20 UTC

    G'day fasoli,

    Hand-crafting your own I/O error messages is tedious and error-prone. In this instance, the die message does not contain sufficient information. Consider using the autodie pragma and let Perl do the work for you.

    Equivalent of your code, giving a basic message only:

    $ perl -e 'open $fh, "<", "not_a_file" or die $!' No such file or directory at -e line 1.

    With autodie you get the same basic message, the I/O operation, and the filename:

    $ perl -e 'use autodie; open $fh, "<", "not_a_file"' Can't open 'not_a_file' for reading: 'No such file or directory' at -e + line 1

    On an unrelated note, it's good that you've used lexical variables for the filehandles, but it's less good that you've given those variables global scope. Limit the scope of those variables like this:

    open my $fh, $mode, $filename;

    See open.

    — Ken

Re: 'open for read' fails as "no such file or directory" but file is there
by fasoli (Beadle) on Nov 02, 2016 at 16:17 UTC

    A different strange thing happens now.

    If I run the script for each $i individually, for example ($i=1; $i<=1 $i++) or ($i=2; $i<=2; $i++) etc, everything is correct and fine. If I run it for $i from 1 to 5 , ($i=1; $i<=5; $i++) , I get the following errors, starting when the script has finished running $i=1, so from $i=2 onwards:

    Argument "Angle:" isn't numeric in sprintf at auto-dash.pl line 51, <$ +input> line 200035. Argument "RB-A1=-1.46"" isn't numeric in sprintf at auto-dash.pl line +51, <$input> line 200035. Argument "label" isn't numeric in sprintf at auto-dash.pl line 47, <$i +nput> line 200036. Argument ""Time" isn't numeric in sprintf at auto-dash.pl line 51, <$i +nput> line 200036. Argument "(ps)"" isn't numeric in sprintf at auto-dash.pl line 51, <$i +nput> line 200036. Argument "label" isn't numeric in sprintf at auto-dash.pl line 47, <$i +nput> line 200037. Argument ""Angle" isn't numeric in sprintf at auto-dash.pl line 51, <$ +input> line 200037. etc *more same lines that go up to <$input> line 400064*

    Also these arguments that it's finding as wrong belong in the first 12 lines of each input file, that it's meant to skip.

    My inputs have only 100001 lines, so for it to say that it's finding errors on line 200000 is by itself strange as there is no such line. This tells me it's somehow creating (??) an input with more lines? Does that again have to do with how I use the open command?

    Please let me know if I've managed to explain this properly or not as I would appreciate any advice on how to solve this :'(

      This comes from the fact that you open but never close your filehandle. $. is reset when the handle is closed, so in your case 0 is the first line of the first file, and perl counts up from that point as if you were reading one big file (and of course the $. < 12 bit only works once).

      (Edit) You can either add an explicit close, or let perl do that automatically by having the handle only exist in the loop, which is done by moving the my-declaration inside the loop (this would be called "lexically scoping the handle to the loop"):

      for my $i (2..12) { for my $k (5..18) { open my $handle, "<", "path" or die; # my means that the $handle +here is a new one on each call. ... # closed is called automatically here } }

      By the way, instead of :

      my @columns = split(/\s+/, $line); # split columns on whitespac +e my $col1 = shift @columns; # column 1 (throw away) my $col2 = shift @columns; # column 2 (throw away) my $col3 = shift @columns; # column 3 (special - keep this one) my $result = sprintf("%8.3f", $col3); # special format for col 3
      You can either write: my (undef, undef, $first, @columns) = split(/\s+/, $line);
      or (undef, undef, my $first, my @columns) = split(/\s+/, $line);,
      whichever you find the easiest to read/understand. It means that the output of split will be placed in the elements of the list on the left side (called left values). Values that are sent to undef will be thrown away, $first is a scalar (you can read the $ as "single") and will take only one value, and since @columns is an array, it will take as many values as possible (all the rest).