How to open the channel and push the data into while loop? - perl

I am trying to traverse the ls | grep command in to the channel, which is while looping and pushing the output to the #output array. But, while loop is not providing the necessary output because of which the code will die with die "could not find the file $_[1] in $Source to $Destination \n" unless(#output);. If I comment the die part of the code and proceed with the copy from source to destination is not working.
Here is part of my code:
my $host ="$hash{Linux_Server_IP}";
my $Uname = "$hash{Linux_Server_Username}";
my $password ="$hash{Linux_Server_Password}";
my $ssh2= Net::SSH2->new();
$ssh2->connect($host)or die "Could not connect : $# \n ";
$ssh2->auth_password($Uname,$password) or die "Could not login : Reason : $#\n";
$ssh2->blocking();
our $chnl = $ssh2->channel();
$chnl->shell();
print $chnl "ls | grep $_[1] \n";
my #output;
sleep(1);
while (<$chnl>){push #output,$_};
die "could not find the file $_[1] in $Source to $Destination \n" unless(#output);
print $chnl "cp $Source $Destination \n";
Error in the log:
could not find the file ABC.csv in /data/Directory1/ABC.csv to /data/Directory2/ABC.csv
I manually verified the file on the server and it does exist. Please advise.

Quoting Net::SSH2::Channel
Alternatively, it is also possible to launch a remote shell (using
shell) and simulate the user interaction printing commands to its
stdin stream and reading data back from its stdout and stderr. But
this approach should be avoided if possible; talking to a shell is
difficult and, in general, unreliable.
Furthermore using ls to detect if a file/directory exists is fragile and unreliable. You should use file test operations instead, e.g.
sub exec_remote($$) {
my($ssh2, $cmd) = #_;
# every exec() requires a new channel
my $chan = $ssh2->channel()
or $ssh2->die_with_error;
# send command to remote
$chan->exec($cmd)
or $ssh2->die_with_error;
# we're done from our side
$chan->send_eof;
# ignore command output
while (<$chan>) {}
# wait for remote command to complete and return its exit status
return $chan->exit_status;
}
sub copy_remote_if_necessary($$$$) {
my($ssh2, $source, $destination, $file) = #_;
if (exec_remote($ssh2, "/usr/bin/test -f ${destination}/${file}") ne 0) {
die "Copy failed!\n"
unless (exec_remote($ssh2, "cp ${source}/${file} ${destination}/") ne 0);
}
}
copy_remote_if_necessary($ssh2, '/data/Directory1', '/data/Directory2', 'ABC.csv');

Related

How to switch different strings from file in a while loop using Perl

I have a question, it is probably an easy one, but I haven't found the answer for it.
I have a file of strings (each string in a separate line, and I need to use each string (line) in the cmd.
I'm using a 'while' loop, but I don't know how to append each string to the loop.
I need to run the following when the XXX.XXX.XXX is the string that needs to be changed in the loop.
c:\putty\putty.exe -ssh "root#XXX.XXX.XXX.XXX" -pw "password" -m "c:\putty\putty.txt"
Try this:
#!/usr/bin/perl
use warnings;
use strict;
open my $fh, "<", "file.txt" or die $!;
while (my $line = <$fh>)
{
chomp $line;
#Here you can replace 'XXX.XXX.XXX' with '$line'. Modify below line as per your requirement.
my $cmd = `c:\\putty\\putty.exe -ssh "root\#$line" -pw "password" -m "c:\\putty\\putty.txt"`;
}
close $fh;
More detailed version would be:
use strict;
use warnings;
my $file = "file_of_strings.file";
# Open file and read contents before closing handle
open(FH, "<", $file) or die "Unable to open \"$file\" $!";
chomp(my #users = <FH>);
close(FH);
for my $user (#users) {
# Frame remote command
my $cmd = "c:\putty\putty.exe -ssh 'root\#${user}' -pw 'password' -m 'c:\putty\putty.txt'";
if (system $cmd != 0) { # system command returns 0 on successful execution
print "Successfully executed command: $cmd\n";
} else {
print "Failed to execute command: $cmd exited $? $!\n"; # Better to log the exit code ($?) and error message, if any($!).
}
}

Create a file in remote linux machine

I want to create a file in remote Linux machine from my local windows machine.
The file which will be created should be of 10 lines
like
E-762334|UC5L0001|2.6.7.1.0.12.0.0.0.372|3.0.0|20130917193000|12
E-762334|UC5L0001|2.6.7.1.0.12.0.0.0.372|3.0.0|20130917194000|13
E-762334|UC5L0001|2.6.7.1.0.12.0.0.0.372|3.0.0|20130917195000|14
E-762334|UC5L0001|2.6.7.1.0.12.0.0.0.372|3.0.0|20130917196001|15
E-762334|UC5L0001|2.6.7.1.0.12.0.0.0.372|3.0.0|20130917193002|6
E-762334|UC5L0001|2.6.7.1.0.12.0.0.0.372|3.0.0|20130917193500|13
E-762334|UC5L0001|2.6.7.1.0.12.0.0.0.372|3.0.0|20130917193700|1
E-762334|UC5L0001|2.6.7.1.0.12.0.0.0.372|3.0.0|20130917193200|13
My code:
use Net::SSH2::Simple;
use Net::SSH2;
my $ssh2 = Net::SSH2::Simple->new();
$ssh2->connect('host') or die "Unable to connect Host $# \n";
$ssh2->auth_password('user','password') or die "Unable to login $# \n";
my $test = <<'END';
E-762334|UC5L0001|2.6.7.1.0.12.0.0.0.372|3.0.0|20130917193000|12
E-762334|UC5L0001|2.6.7.1.0.12.0.0.0.372|3.0.0|20130917194000|13
E-762334|UC5L0001|2.6.7.1.0.12.0.0.0.372|3.0.0|20130917195000|14
END
my $cmd="echo $test > /data/spanda/pppp1";
($stdout,$stderr, $exitcode) = $ssh2->cmd($cmd ,'bufsize' => 4_096 ) or die "cannot execute command: ";
if ($exitcode == 0) {
print $stdout if $stdout;
print $stderr if $stderr;
} else {
print $stderr if $stderr;
die "command failed with exit code $exitcode";
}
I am getting Error like
13: Command not found.
E-762334: Command not found.
UC5L0001: Command not found.
2.6.7.1.0.12.0.0.0.372: Command not found.
3.0.0: Command not found.
Your command doesn't work for the same reason as echo foo|bar > /tmp/x doesn't work (strings with pipes should be quoted).
On the other hand there is more robust method; Net::SSH2::Simple inherits from Net::SSH2 which in turn can return Net::SSH2::SFTP object,
use Fcntl;
# my $sftp = $ssh2->sftp();
my $fh = $ssh2->sftp->open('/data/spanda/pppp1', O_WRONLY|O_CREAT) or die;
print $fh $test;

Perl Script Help | Readthrough group of files, looking for specific match

Here is what im trying to achieve.
I have a perl script that looks into a group of directly parse the files it finds and locates a specific string. If it finds the specific string, it ignores it, if it finds the first part of the string, but the second part doesnt match, it writes it out to a log file.
The part im stuck with, is how to read the entire file in, and look to see if the string exists in the entire file.
Some background, im trying to write a script that will read cisco rancid files, and looks for the sys logging details. These are stored in the config as such
logging x.x.x.x
where x.x.x.x is the syslog IP address.
Currently, i can read in the file, check it line by line, and see if the file contains logging x.x.x.x, if it does, it ignores it.
If it contains logging y.y.y.y (where y is an IP different to what it should be), it will write out to a log file, that the logging is setup, but incorrectly configured. But i cant for the life of me work out how to get it to read the entire file, and if logging x.x.x.x or even logging y.y.y.y doesnt exist to write out that it is not configured.
script is below
#!/usr/bin/perl
use strict;
use warnings;
#Syslog Checker.... V0.0.1
##Config Items
my $basedir = "/home/srelf/Documents/Projects/perl/Configs";
my #verdir = qw(sw_a);
my $fulldir;
my $configs;
my $loghost = "x.x.x.x";
my $combidir;
use POSIX qw(strftime);
$now_string = strftime "%a%b%e%Y", gmtime;
open FILE, ">>Configchecker.$now_string.txt" or die $!;
print FILE "### Logging Host Settings ###\n";
close FILE;
foreach $combidir (#verdir) {
$fulldir = "$basedir/$combidir";
opendir( DIR, $fulldir );
my #files = grep { $_ ne '.' && $_ ne '..' } readdir DIR;
closedir(DIR);
while ( #files > 0 ) {
$configs = pop #files;
# print "$fulldir/$configs\n"; # used for debug shows current file with full path.
open FH, "$fulldir/$configs" or die $!;
#dataarry = <FH>;
foreach my $line (#dataarry) {
# Start an if statement, the condition of which is
# "If this particular line contains logging + IP address."
if ( $line =~ m/logging \b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/i ) {
#then if the line located above contains logging and the log host ignore it
if ( $line =~ m/logging $loghost/i ) {
}
# if the above line contains an ip but it is not the correct ip do the below.
elsif ( $line =~
m/logging \b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/i )
{
open FILE, ">>Configchecker.$now_string.txt" or die $!;
print FILE "$configs\n";
print FILE
"Logging for this device is set, but pointing at the wrong host: $line\n";
close FILE;
} # End the if condition here.
}
}
} # End the foreach loop here;
open FILE, ">>Configchecker.$now_string.txt" or die $!;
print FILE "### NTP Settings Check ###\n";
close FILE;
}
Thanks in advance for any help.
Im very new to perl, this is my first shot.
Steve.
foreach my $configs (#files)
{
my $CONFIGURED=0;
open FH, "$fulldir/$configs" or die $!;
while (<FH>)
{
if ($_ =~ m/logging/)
{
$CONFIGURED++;
}
if ($_ =~ m/logging \b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/i and $_ !~ m/logging $loghost/i)
{
print "Logging for this device is set, but pointing at the wrong host: $_\n";
}
}
if ($CONFIGURED == 0)
{
print "NOT CONFIGURED $configs\n";
}
}

Why does this program fail to copy files?

this morning, my friend and I discussed and wrote the below code. The idea behind this Perl script is to create the directory structure and copy the files to the corresponding directory.
#!/usr/bin/perl
use File::Path;
use File::Copy;
use Path::Class;
use File::Basename qw/dirname/;
my $src = "/Vijay/new.txt";
unless (open(MYFILE, "file1")) {
die ("cannot open input file file1\n");
}
$line = <MYFILE>;
while ($line ne "") {
print ($line);
mkdir_and_copy($src,$line);
$line = <MYFILE>;
}
sub mkdir_and_copy {
my ($from, $to) = #_;
my($directory, $filename) = $to =~ m/(.*\/)(.*)$/;
print("creating dir $directory");
system "mkdir -p $directory";
print("copying file $from to $to");
system "cp -f $from $to";
return;
}
The above piece of code creates the directory structure, but fails to copy the files to the corresponding directory. Could you please let us know, where exactly we are wrong?
Contents of file1:
test/test1/test2/test.txt
Contents of new.txt:
Shell/Test/test1/test1.txt
Shell/Test/test2/test2.txt
Shell/Test/test3/test3.txt
Output:
> ./mypgm.pl
test/test1/test2/test.txt
creating dir test/test1/test2/copying file /Vijay/new.txt to test/test1/test2/test.txt
cp: cannot access /Vijay/new.txt: No such file or directory
>
The directory Vijay has the file new.txt with the above mentioned content.
Thanks in advance,
Vijay
Hello everyone,
I just modified my code. Please refer the below section of code.
#!/usr/bin/perl
use File::Path;
use File::Copy;
use File::Basename qw/dirname/;
my $src = "./Vijay/new.txt";
unless (open(MYFILE, "file1"))
{
die ("cannot open input file file1\n");
}
$line = ;
while ($line ne "")
{
print ($line); print("\n");
mkdir_and_copy($src,$line);
$line = ""; }
sub mkdir_and_copy
{
my ($from, $to) = #_;
my($directory, $filename) = $to =~ m/(.\/)(.)$/;
$temp = $directory.$filename;
print("Creating dirrectory $directory \n");
if(! -d $directory)
{
mkpath($directory) #or die "Failed to create path";
}
printf("From: $from \n");
printf("To: $temp \n");
copy($from,$temp) or die "Failed to Copy";
return;
}
Now, it creates the exact directory structure and copies the file to the corresponding directory. Could you please tell me that, whether the above code is a proper one?
Your goal is not clear to me, but perhaps this will help you solve the problem:
# Perl scripts should always include this.
# Your original script was generating some useful warnings.
use strict;
use warnings;
my $src = "/Vijay/new.txt";
my $f1 = 'file1';
# This is the recommended way to open a file --
# that is, using a lexical file handle.
open(my $file_handle, '<', $f1) or die "open() failed : $f1 : $!";
# This is the typical way of iterating over the lines in a file.
while (my $line = <$file_handle>){
# You probably want to remove the newline
# before passing the line to mkdir_and_copy()
chomp $line;
mkdir_and_copy($src, $line);
}
sub mkdir_and_copy {
my ($from, $to) = #_;
my ($directory, $filename) = $to =~ m/(.*\/)(.*)$/;
# When writing a script that makes system() calls,
# start by simply printing them. After everything
# looks good, convert the print commands to system() calls.
print "system(): mkdir -p $directory", "\n";
print "system(): cp -f $from $to", "\n";
# The return is not needed.
}
When I run the script with the inputs you provided, here's the output:
system(): mkdir -p test/test1/test2/
system(): cp -f /Vijay/new.txt test/test1/test2/test.txt
This can't be your intent. In particular, why are you iterating over file1 when it contains only one line? Perhaps you meant to iterate over new.txt?
The first thing to do if something "does't work" is to catch errors and to look at them. Then to investigate content of variables. In your case the variable $to just contains the file name, so the script copies it into the current working directory, I'd imagine, not into the newly created directory.
HOWEVER, the methods you're using to get your job done are not exactly the best. It would be better to actually use File::Path and File::Copy, and in particular your way of splitting a path into directory and filename at the first slash is anything but general. This sort of thing should be done in libraries, of which Perl has many.
I'll bet your $line variable still has a newline appended to it. The input returned from the filehandle input operator (<MYFILE>) includes the record separator (usually the newline character(s) for your OS). Try this:
$line = <MYFILE>;
chomp($line);

Does the -f file test operator work in mod_perl?

I'm trying to use the Tenjin module but it fails because it can't find the template file but it exists. I've added some debug statements into the module and it's not passing
return $filepath if (-f $filepath);
even when $filepath is correct. I've tried in a standalone script and it works fine but when I copy it to the mod_perl script it fails. Any ideas?
$filepath is a full absolute path: /something/another/dir/2/filename.plhtml
This is the function form the module. Notice my "Debug"...it prints the correct path to the file which is 777 but it never prints YES.
sub find_template_file {
my ($this, $filename) = #_;
my $path = $this->{path};
if ($path) {
my $sep = $^O eq 'MSWin32' ? '\\\\' : '/';
foreach my $dirname (#$path) {
my $filepath = $dirname . $sep . $filename;
print STDERR "--$filepath--\n";
if (-f $filepath){
print STDERR "--- YES ---\n\n";
}
return $filepath if (-f $filepath);
}
} else {
return $filename if (-f $filename);
}
my $s = $path ? ("['" . join("','", #$path) . "']") : '[]';
die "Tenjin::Engine: $filename not found (path=$s).";
}
Fails with
Tenjin::Engine: index.plhtml not found (path=['/var/2.0/templates/search']). at /usr/lib/perl5/site_perl/5.8.8/Tenjin/Engine.pm line 56.\n
The Apache process also needs read and execute access on every subdirectory up to the full path. (If symbolic links are involved, it will be trickier to determine what the accesses are).
If you can debug the script in place on the web server, you might want to get Perl to deliver you an error message:
if (! -f $filename) {
open(ACK, "<", $filename);
print STDERR "Couldn't open $filename because of: $!\n";
}
-f will return false if the file doesn't exist but undef if the stat call failed for some other reason.
Test if the return is defined and if it is not, show the error that will have been set in $!.
That may give you a clue.
Give -f the full path to the file, and make sure it is readable by Apache.
Are you using absolute or relative pathnames? Your assumptions about the current directory may simply be wrong.
I'm going to totally ignore what you asked and answer something completely different instead! I'm just that crazy!
Well, not really, I'm leveraging a core perl module, File::Find, instead of writing my own directory parser.
On request, here's the question I'm actually answering:
"How do I find the path to a file that is somewhere in a sub-directory of a specific set of paths?"
use File::Find;
# Other parts of the class here
sub find_template_file {
my ($this, $filename) = #_;
my $file_path;
my $path = $this->{path};
# Note that this inner sub uses variables we defined above
find(sub {
if ($_ eq $filename)
$file_path = $File::Find::name;
}, #$path);
if ($file_path)
return $file_path;
my $s = $path ? ("['" . join("','", #$path) . "']") : '[]';
die "Tenjin::Engine: $filename not found (path=$s).";
}