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

Sirs,

We have an Automated FTP script that pulls file from a server. This Script sometimes works without any error but sometimes is unable to pull even a single file. In case of such error, we manually (from DOS prompt) connect(FTP) to Unix based server and get files through "mget" command. This means that network and files are proper, but why is the script unable to pull with first error_msg as "Bad file descriptor" and all the other files give error_msg as "No such file or directory".


The relevent portions of the script are below:
===============================
####Connect to Server open (FD1, ">>$logname") || print FD1 "Couldnt open - write $logname\n +" ; my $ftp=new Net::FTP("$server",Timeout=>"300000")|| print FD1 "Can not + connect to ftp server\n"; $ftp->login("$username","$passwd")|| print FD1 "Can not login to ftp s +erver\n"; ..... ...... my @files=$ftp->dir or print FD1 "error in dir list--to var-- $dir \n" +; .... ..... foreach(@files) { #print "$_\n"; $_=substr($_,41); s/ */ /g; s/^ *//g; chomp; print FD1 "Getting $_\n"; $filename2=$_; $filename=substr($filename2,0,12) ; .... ... if( -f $first_check) { print FD1 "$filename exist, So no need to download \n"; } else { ..... $ftp->binary(); $ftp->get($filename2) or $newerr=1; #|| print "$filename2 can not be downloaded error $!\n"; if ($newerr) { print FD1 "$filename2 can not be downloaded error $!\n"; } else { print FD1 "Got file $filename2 \n"; ...... ..... } }
=============================
The relevent Log file output is below:
============================
TTFILE008498 exist, So no need to download Getting TTFILE008499;1 TTFILE008499 exist, So no need to download Getting TTFILE008500;1 TTFILE008500;1 can not be downloaded error Bad file descriptor Getting TTFILE008501;1 TTFILE008501;1 can not be downloaded error No such file or directory Getting TTFILE008502;1 TTFILE008502;1 can not be downloaded error No such file or directory
================================
Here the file that needs to be downloaded should not exists (first_check), and then download in binary mode. We are unable to pull a single file somtimes, but n/w and file are fine.

Please suggest.

Ritu Nigam.

20040514 Edit by castaway: Changed title from 'FTP script to get bin files from Unix server, after 1st file error "Bad file descriptor" rest files also error but all files can be manually pulled by mget at DOS-prompt.'

Replies are listed 'Best First'.
Re: Bad file descriptor' error with automated FTP script.
by mifflin (Curate) on May 14, 2004 at 07:09 UTC
    I haven't used the dir() method in Net::FTP before but, by looking at your code, it appears that it returns far more that you need by the way you are parsing out the file name. Is it possible that your code is not getting the correct file name?
    Try the ls() method instead.
    my $files = $ftp->ls(); # this returns an arrayref for my $filename (@$files) { # put your code in here to deal with each file }
      The command "dir" gives the equalent of "ls -l" (long list) and "ls" only gives me the filename, In my script the date check is also there. As you can see the logs --"getting ...." is the file name and also in "... exists, so no need to download"

      ============================
      Getting TTFILE008499;1 TTFILE008499 exist, So no need to download
      ========================
        I don't see you using the date/time there. (Surely that -f gets a filename?). In any case, you could check if the parsing of the dir() output is producing rubbish.

        If you need the last modification time of the file, there is also a mdtm(Filename) command which will give you that.

        Also, adding a Debug => 1 to your new() call may give some helpful clues. C.

Re: Bad file descriptor' error with automated FTP script.
by castaway (Parson) on May 14, 2004 at 07:18 UTC
    I was about to suggest ls instead of dir, but it looks like mifflin beat me to it. So instead I'll point out that your '|| print .. ' statements are not ending the script, thus you will get errors in all the following code, if for example your $ftp = new Net::FTP .. fails. (And assuming you are using at least the 'warnings pragma, else it will just complain siliently.)

    C.

      For "||print .." changed to "or die ..." , this should be fine.
Re: Bad file descriptor' error with automated FTP script.
by allyc (Scribe) on May 14, 2004 at 10:23 UTC

    You might be able to get more information from Net::FTP.

    Any messages, errors or other wise that are returned by the FTP Server can be retrieved by calling $ftp->message(). You might find that there is some error in there, such as permission denied that is causing your problem.

    HTH.

    Alistair

      Note also that $! will not contain any meaningful result unless the function you have just called explicitly claims to set it. Otherwise its just the value left over from the last failed system call your code happened to make, which may or may not be related to the error you're getting.
Re: Bad file descriptor' error with automated FTP script.
by graff (Chancellor) on May 15, 2004 at 03:34 UTC
    I think the problem is in the way that you try to parse the ftp "dir" output -- you have this:
    my @files=$ftp->dir or print FD1 "error in dir list..." ... foreach(@files) { ... $_=substr($_,41); ... }
    There's no guarantee that each line of output from "dir" will have the file name starting at the 41st character. If the file size (which is the fourth column in the dir output) happens to be really large (more than 7 digits, I think), then that column will be wider than on other lines, and the file name will be pushed that many characters further to the right.

    Use "split()" instead -- the number of columns output by "dir" is always the same:

    my @files = $ftp->dir or die "ack -- can't get ftp->dir"; for ( @files ) { my ($mode, $nlnk, $grp, $sz, $mo, $dy, $yrtm, $fname) = split( /\s+ +/, $_, 8 ); ... }
    This let's you have a variable assigned to every piece of information you could possibly use (and some that you will probably never use) -- and you don't need "chomp" here.

    Note the use of the "8" in the split call -- this will make sure that we stop splitting at the 8th "word", so if some jerk puts up a file with spaces in the file name, split() will still treat the whole name as one "word" to be assigned to $fname.

      Thanx graff, the spilt is working fine.