Ftp code in Perl

Hi All, i want to have a ftp function in my perl but i am unfamailiar with the perl syntax. Can any body help ?

The ftp code below is in csh, can anybody help to convert this to perl with the same functionality ?

foreach t (10.10.10.10 20.20.20.20)

set USER = "xxx"
set PASS = "zzz"
ftp -n $t <<XX > /dev/null
user $USER $PASS
cd /bbb/ccc
lcd /kkk
prompt
ascii
mget *txt
XX

end

You could make use of the following script which is very basic in nature without any customizations and necessarily critical factors for ftp being configured in a configuration file.

#! /opt/third-party/bin/perl

use Net::FTP;

my ($server, $user, $pass, $trn_mode, $cwd, $file, $debuglvl);
my ($confFile, $content, @split_line);

$confFile = "ftp.conf";

sub infoCheck {
  my $name = shift;
  my $value = shift;

  if( $name eq "debug" ) {
    if( $value != 0 && $value != 1 ) {
      #Restrict them to 0 or 1
      $value = 0;
    }
    return $value;
  }

  if( length($value) <= 0 ) {
    print "No Info for $name. Terminating\n";
    close(FILE);
    exit 1;
  }
}

open(FILE, "< $confFile") || die "Unable to read conf file: $confFile <$!>\n";
while( chomp($content = <FILE> ) ) {
  @split_line = split(/=/, $content);
  if( $split_line[0] eq "server" ) {
    infoCheck ("server", $split_line[1]);
    $server = $split_line[1];
  }
  elsif( $split_line[0] eq "user" ) {
    infoCheck ("user", $split_line[1]);
    $user = $split_line[1];
  }
  elsif( $split_line[0] eq "pass" ) {
    infoCheck ("pass", $split_line[1]);
    $pass = $split_line[1];
  }
  elsif( $split_line[0] eq "mode" ) {
    $mode = $split_line[1];
  }
  elsif( $split_line[0] eq "cwd" ) {
    $cwd = $split_line[1];
  }
  elsif( $split_line[0] eq "file" ) {
    infoCheck ("file", $split_line[1]);
    $file = $split_line[1];
  }
  elsif( $split_line[0] eq "debug" ) {
    $debuglvl = infoCheck ("debug", $split_line[1]);
  }
  else {
    print "Whatz this unknown conf value!!! ???\n";
    close(FILE);
    exit 1;
  }
}
close(FILE);

$ftp = Net::FTP->new($server, Debug => $debuglvl ) || die "Unable to connect to $server $@ \n";

$ftp->login($user, $pass) || die "Unable to login. ", $ftp->message;

if( length($cwd) > 0 ) {
  $ftp->cwd($cwd) || die "Unable to cwd. ", $ftp->message;
}

if( $mode eq "ascii" ) {
  $ftp->ascii || die "Unable to set mode to ascii. ", $ftp->message;
}

elsif( $mode eq "binary" ) {
  $ftp->binary || die "Unable to set mode to binary. ", $ftp->message;
}

$ftp->get($file) || die "Unable to get file. ", $ftp->message;

$ftp->quit;

exit 0
>cat ftp.conf
server=server1
user=user1
pass=password1
mode=ascii
cwd=somedir
file=somefile
debug=0

Please do let us know if there are any problems with that. :slight_smile:

You can use Net::FTP for that. Look at the example on the manpage, it basically demonstrates all of the things you will need. e.g. login, cwd, get etc.

Hi cbkihong,

Is there a perl code with same function as the command lcd in csh ?
And how can i put a wild card in the get command ? $ftp->get("xxx*.txt"). It didn;t seem to work below for me with the asterix

#!/usr/bin/perl
use Net::FTP;

    $ftp = Net::FTP->new("10.10.10.10", Debug => 0)
      or die "Cannot connect to some.host.name: $@";

    $ftp->login("aaa",'bbb')
      or die "Cannot login ", $ftp->message;

    $ftp->cwd("/myreports")
      or die "Cannot change working directory ", $ftp->message;

    $ftp->get("xxx*.txt")
      or die "get failed ", $ftp->message;

    $ftp->quit;
1 Like

Can anybody help me with the above ? I would want to lcd to my desired directory and get all files with xxx*.txt where asterix is the wildcard

Try the chdir() function, if you really want that.

http://perldoc.perl.org/functions/chdir.html

Thanks for your suggestion. What about wildcards ? Can i use wild card to get the files that i want as shown below? . Eg xxxabc.txt , xxxbbb.txt , xxxrrr.txt will all be ftped.

$ftp->get("xxx*.txt")
or die "get failed ", $ftp->message;

Wildcard is typically implemented by FTP clients, by doing an ls and then expanding the glob itself to arrive at the final list of matching files. The FTP protocol specification does not specify wildcards at all, so it is not surprising the module doesn't support it. You will need to do your own processing with ls.

Do u mean using ls in this perl module ?
Can you show me an example ?

If you look at the documentation:

my @lines = $ftp->ls();

@lines will contain a list of entries for that dir, including '.' and '..' and all hidden '.xxxxx' files and dir. You may not wish to include them. Just filter them as you need for your circumstances.

There are many ways to filter the content of an array, but I will show you my favourite way (you will see I use a lot on this forum). For instance, this is to exclude all entries starting with a dot, and getting the rest over FTP in a loop (untested):

foreach my $file ( map { (/^[^\.].+$/) ? $_ : () } @lines ) {
     $ftp->get($file);
}

By the way, I just found a wrapper to Net::FTP (called Net::FTP::Simple) that supports retrieving a set of files based on a regular expression (look for file_filter in the doc). It uses Net::FTP as the core. This module is not a builtin module. You need to install it separately if you want to use it. Again, I have no experience with it as I just saw it by coincidence.

Hi cbkihong,

I actually followed yr code but in the end i didn;t get to ftp anything. Is the ftp session too fast, because the number of files i need to ftp is like close to 100 files.
Can you give me some advice ?

#!/usr/local/bin/perl

use Net::FTP;

foreach $t ( 10.10.10.10   20.20.20.20 ) {
                chdir "$script_dir";
                print "Now Processing $t ...\n";

                $ftp = Net::FTP->new("$t", Timeout=>240, Debug => 0)
                or die "Cannot connect to some.host.name: $@";

                $ftp->login("userid",'password')
                or die "Cannot login ", $ftp->message;

                $ftp->cwd("/myreports")
                or die "Cannot change working directory ", $ftp->message;

                my @lines = $ftp->ls(<*sorts>);
                        foreach my $summary_names ( map { (/^[^\.].+$/) ? $_ : () } @lines ) {
                                $ftp->get($summary_names);
                        };

                $ftp->quit;
        };

I think <*sorts> is getting globbed locally. Try just putting it in single quotes; you don't want it to be evaluated at all, just sent to the remote server.

Hi Era,

I think the below code works.
But it seems that the code tries to ftp everything in the folder including file names like AAA_sorts_inter , BBB_sorts_inter and so on.
My required files to be ftped over should only be of these names like AAA_sorts, BBB_sorts

 $ftp->ls(<*sorts>);

Since you are filtering the list anyway, why not filter it down to just the ones you want.

my @lines = grep { /^([^.].*)?sorts$/ } $ftp->ls();
foreach my $name (@lines) {
    $ftp->get($name);
}

(Beats me why cbkihong suggested to use map here; grep would seem to me like the correct operator.)

Hi Era,

Thanks a million. Your code works !!
But i am new to Perl and the below expression seems very complex.
Perhaps you can explain what does the below character's purpose when use in filtering to allow me to comprehend better.

my @lines = grep { /^([^.].*)?sorts$/ }

[^.] matches any character except dot (or newline, implicitly)
.* matches any character (except newline) zero or more times, i.e. "any string of any length"
the question mark makes the whole thing optional, so just "sorts" with nothing before it matches too.

Hi Era,

Thanks for the explanation.
But what if i need to mget the files below.
In Csh , i write it as the below where "ME" is a fixed string and $var is a csh variable.
What would be the code for the below in Perl ?

mget ME*$var*sorts

Hi Era,

Tried using the below but its doesn;t work.
Can you give some advice ?

grep { /^ME.?$var.?sorts$/ }

The regex for "any string" is .* not .?

Hi Era,

Great thanks!! Its working!
What if i need either $var or $myfile ?
I tried to use the below but it doesn;t work though.
Can you help

grep { /^ME.*($var|$myfile).*sorts$/ }