Create a file in remote linux machine - perl

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;

Related

Parallel::ForkManager gives - child process STDIN is not a real system Error

I have a script which does ssh to number if servers(#hosts) stored in an array and executes certain commands on those hosts.
...
use Net::OpenSSH;
use Parallel::ForkManager;
#these hosts are the IP addresses
my #hosts = ("host1", "host2", "host3", "host4", "host5");
my $ssh;
my $command = "perl -e "print \"Hello..\"";
my $pm = Parallel::ForkManager->new(5);
LOOP:
foreach my $n (#hosts) {
my $pid = $pm->start and next LOOP;
#doing ssh to the host and running the command
#lets say here I am executing simple Perl command
$ssh = Connect($n, "user", "passwd");
$ssh->capture($command);
$pm->finish;
}
$pm->wait_all_children;
undef $ssh;
sub Connect {
my ( $host, $user, $passwd ) = #_;
my $ssh = Net::OpenSSH->new($host, user=>$user, password=>$passwd);
$ssh->error and die "Couldn't establish SSH connection: " . $ssh->error;
return $ssh;
}
...
When I execute this script, I am getting below error which is like nightmare to me.
child process STDIN is not a real system file handle at (eval 2) line 145
I don't why this error occurs. Could someone please help me to resolve this issue.
Am I missing something in $pm->finish or do I need to add $pm->run_on_finish(..) ?

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

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');

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($!).
}
}

Running a non-perl script into my perl script? [duplicate]

This question already has answers here:
How do I get the output of an external command in Perl?
(7 answers)
Closed 8 years ago.
I am writing a Perl script to automate some software installation.
In my script I run another bash script and take its output and print it again.
print `/home/me/build.sh`;
but build.sh script take 8 minutes, so my script wait till the 8 minutes and script finishes the starting in printing the output.
How can I print each line from the build.sh program as it is running in bash shell?
As the comment below I use system ("/home/me/build.sh");
but the output goes to shell however I make out redirection in my script to my log file,
open $fh, "> filename";
*STDOUT = $fh;
*STDERR = $fh;
Then should when I use system function its output will be redirected to filename, but it isn't.
Should I use print system ("/home/me/build.sh"); instead of system ("/home/me/build.sh");?
#
The full code:
#!/usr/bin/perl
use strict;
use warnings;
use IO::File;
my %DELIVERIES = ();
my $APP_PATH = $ENV{HOME};
my $LOG_DIR = "$APP_PATH/logs";
my ($PRG_NAME) = $0 =~ /^[\/.].*\/([a-zA-Z]*.*)/;
main(#argv);
sub main
{
my #comps = components_name();
my $comp;
my $pid;
while ( scalar #comps ) {
$comp = pop #comps;
if ( ! ($pid = fork) ) {
my $filename = lc "$LOG_DIR/$comp.log";
print "$comp delpoyment started, see $filename\n";
open (my $logFile, ">", "$filename") or (die "$PRG_NAME: $!" && exit);
*STDOUT = $logFile;
*STDERR = $logFile;
deploy_component ( $comp );
exit 0;
}
}
my $res = waitpid (-1, 0);
}
sub components_name
{
my $FILENAME="$ENV{HOME}/components";
my #comps = ();
my $fh = IO::File->new($FILENAME, "r");
while (<$fh>)
{
push (#comps, $1) if /._(.*?)_.*/;
chomp ($DELIVERIES{$1} = $_);
}
return #comps;
}
sub deploy_component
{
my $comp_name = shift;
print "\t[umask]: Changing umask to 007\n";
`umask 007`;
print "\t[Deploing]: Start the build.sh command\n\n";
open (PIPE, "-|", "/build.sh");
print while(<PIPE>);
}
A more flexible way is to use pipe.
open PIPE, "/home/me/build.sh |";
open FILE, ">filename";
while (<PIPE>) {
print $_; # print to standard output
print FILE $_; # print to filename
}
close PIPE;
close FILE;
BTW, print system ("/home/me/build.sh"); will print the return value of system(), which is the exit status of your shell script, not the output wanted.
How can I print each line from the build.sh program as it is running in bash shell?
Possible Solution:
You can try the following
system ("sh /home/me/build.sh | tee fileName");
The above statement will show the output of build.sh on the console and at the same time write that output in the filename provided as the argument for tee

Perl telnet does not wait for the end of the previous command

Perl telnet does not wait for the end of the previous command. In the file infile.log I see the continuation of the command my # config = $ telnet-> cmd ("sh run");, but the script is already beginning to run the command print ("Format configuration", $ _, "\ n");. As a result, I get an empty array #config.
foreach (#linksys_sps){
print ("Connecting to ",$_,"\n");
my $telnet = new Net::Telnet ( Timeout=>10,Errmode=>'return',Input_Log => "infile.log");
$telnet->open($_);
if ($telnet->errmsg){
print "Can't connect to " . $_ . " Error: " . $telnet->errmsg . "\n";
} else {
$telnet->max_buffer_length(5 * 1024 * 1024);
$telnet->waitfor('/User Name:$/i');
$telnet->print('admin');
$telnet->waitfor('/Password:$/i');
$telnet->print('password');
print ("Set Terminal Variable ",$_,"\n");
$telnet->cmd("terminal datadump");
print ("Create file ",$_,"\n");
my $file = sprintf($folder."/".$_);
print ("Create file ",$file,"\n");
system "touch $file";
system "chown nobody $file";
print ("Read configuration ",$_,"\n");
my #config = $telnet->cmd("sh run");
print ("Write configuration ",$_," to file ",$file,"\n");
open my $fh, "> $file" or die "Can't open $file : $!";
foreach (#config) {
print $fh "$_"; # Print each entry in our array to the file
}
close $fh;
print ("Set Terminal Variable ",$_,"\n");
$telnet->cmd("no terminal datadump");
$telnet->cmd("exit");
}
}
How to fix it?
From the docs:
The methods login() and cmd() use the prompt setting in the object to
determine when a login or remote command is complete. Those methods
will fail with a time-out if you don't set the prompt correctly.
In otherwords, you need to add the prompt parameter when you create the $telnet object:
my $switch_name = 's-east';
my $telnet = new Net::Telnet (
Timeout=>10,
Errmode=>'return',
Input_Log => "infile.log",
Prompt => '/\Q$switch_name\E#\s?$/'
);
(It looks for the shell prompt to know that a command is completed).
Due to the instability of the cmd(), I switched to using waitfor(String => $string).
New script below:
foreach (#linksys_sps){
my $file = sprintf($folder."/".$_);
print ("Create file ",$file,"\n");
system "touch $file";
my $string = sprintf($_."#");
print ("Prompt String is ",$string,"\n");
print ("Connecting to ",$_,"\n");
my $telnet = new Net::Telnet (
Timeout=>20,
Errmode=>'return',
Input_Log => "infile.log"
);
$telnet->open($_);
if ($telnet->errmsg){
print "Can't connect to " . $_ . " Error: " . $telnet->errmsg . "\n";
} else {
$telnet->waitfor('/User Name:$/i');
$telnet->print('admin');
$telnet->waitfor('/Password:$/i');
$telnet->print('password');
$telnet->waitfor(String => $string );
print ("Set Backup Terminal Variable ",$_,"\n");
$telnet->print("terminal datadump");
$telnet->waitfor(String => $string );
print ("Read configuration ",$_,"\n");
$telnet->print('sh run');
my #config = $telnet->waitfor(String => $string );
print ("Write configuration ",$_," to file ",$file,"\n");
open my $fh, '>', $file or die "Cannot open file: $!";
foreach my $config (#config) {
print $fh "$config\n";
}
close $fh;
print ("Set Default Terminal Variable ",$_,"\n");
$telnet->print('no terminal datadump');
$telnet->waitfor(String => $string );
$telnet->print('exit');
}
}