2

Previously I have asked a question here on how to determine whether a path is a directory or not in remote site using SSH. I wish to create the directory if the path is not a directory. I have tried following code with two ways but it seem not to be working. Thanks for everyone that helps here.

use File::Path;

my $destination_path = "<path>";
my $ssh = "usr/bin/ssh";
my $user_id = getpwuid( $< );
my $site = "<site_name>";
my $host = "rsync.$site.com";

if (system("$ssh $user_id\@$host [ -d $destination_path ]") == 0) {
    print "It is a directory.\n";   

} else {
    print "It is not a directory.\n";

    #First Way
    if(system("$ssh $user_id\@$host [ make_path ($d_path_full) ]") == 0{

    #Second Way
    if(system("$ssh $user_id\@$host [ mkdir -p $d_path_full ]") == 0{
       print "Create directory successfully.\n";

    } else {
       print "Create directory fail.\n";
    }
}
GMB
  • 216,147
  • 25
  • 84
  • 135
Yih Wei
  • 537
  • 3
  • 21

2 Answers2

5

The bracket(s), single [ or the pair [ ], is a builtin in bash which is a test operator (see man test), and the last use of it is incorrect. But you don't need it to make a directory

use warnings;
use strict;
use feature 'say';

my $ssh = '/usr/bin/ssh';

my $user_id = ...
my $host    = ...

my $to = quotemeta $user_id.'@'.$host;

my $cmd = 'mkdir -p TEST_MKDIR_OVER_SSH';

system("$ssh $to $cmd") == 0  or die "Can't mkdir: $!";

The mkdir is quiet with -p if a directory already exists, and it returns succes what also defeats the purpose of [ ] (if that was the intent). But an actual error -- a file with that name exists, no permissions on the path, etc -- does make its way back to the script, as you'd want, and a string with the error message is in $! so please test for this.

If you simply wish to know whether the directory existed please put back your test branch, or just omit -p and analyze the $! for what that message is on your system.

As for the second attempt: the command to be executed runs on the remote system and has nothing to do with this script anymore (apart from interpolated variables). So Perl functions or libraries from this script make no sense in that command.

For the next step I suggest to look into modules for (preparing and) running external commands, that are much more helpful than the bare system.

Some, from simple to more capable: IPC::System::Simple, Capture::Tiny, IPC::Run3, IPC::Run. Also see String::ShellQuote, to prepare commands and avoid quoting issues, shell injection bugs, and other problems. This recent post is a good example, and there's a lot more out there.

zdim
  • 64,580
  • 5
  • 52
  • 81
2

I would recommend using a proper module to do SSH, namely Net::OpenSSH, a SSH client built upon OpenSSH.

While being implemented in pure Perl, it is fast and stable, and has no mandatory dependency (apart of course, OpenSSH binaries). As explained in the docs, it will, under certain conditions, automatically quote any shell metacharacters in the command lists.

The following codes demonstrates how it can respond to your use case. It relies on the same shortcut explained by @zdim, using mkdir -p :

  • if the directory does not exists, it gets created (if that fails, an error happends)
  • if it already exists, nothing happens
  • if a file exists with the target name, an error happens

Code :

use warnings;
use strict;
use Net::OpenSSH;

my $host = ...;
my $user_id = ...;
my $destination_path = ...;

# connect
my $ssh = Net::OpenSSH->new($host, user => $user_id);
$ssh->error and die "Can't ssh to $host: " . $ssh->error;

# try to create the directory
if ( $ssh->system('mkdir', '-p', $destination_path) ) {
    print "dir created !\n";
} else {
   print "can't mkdir $dir on $host : " . $ssh->error . "\n";
}

# disconnect
undef $ssh;
GMB
  • 216,147
  • 25
  • 84
  • 135
  • Hi @GMB Thanks for your answer. It is not working as my working machine do not have the module for Net::OpenSSH installed and I am not allowed to install any module in the machine. Sorry. – Yih Wei Dec 21 '18 at 09:25