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

Hi Monks,

I'm using the Net::SSH2 library for specific reasons. If I wish to copy a file from a remote host to local client, I can use scp_get() but this function won't do directory copies (and certainly not recursive copy). So I need to check before copying if a file is a dir or symlink etc. In perl its easy with file test operators. But they don't work with files on a remote host!

So I'm trying to use Net::SSH2::SFTP->stat() on each file to determine it's type. I see it returns an argument 'mode' which usually gives me an integer 16877 for directories, and 33188 for regular files. All I can think is that these are codes of some sort relating to the permissions. But the docs are scant on this. If it really is the case that directories ALWAYS return this value, then I can use this as a file test operator, but I'm very hesitant to do this without checking on other systems.

Any suggestions would be greatly appreciated!
James

Replies are listed 'Best First'.
Re: Net::SSH2::SFTP stat function
by salva (Canon) on Oct 01, 2008 at 19:38 UTC
    Read the docs for the Perl builtin stat regarding the "mode" field.
    use Fcntl ':mode'; my $mode = ...; if (S_ISDIR($mode)) { # directory ... } elsif (S_ISLNK($mode)) { # link ... } elsif (...
    Also, instead of Net::SSH2, you should try using Net::SFTP::Foreign that has methods to mirror full file systems trees:
    use Net::SFTP::Foreign; my $sftp = Net::SFTP::Foreign->new($host); $sftp->error and die "unable to connect to remote host: ".$sftp->error +; $sftp->rget($remote_dir, $local_dir);
      Thanks for the suggestion! I did as you suggested:

      use Fcntl ':mode'; my $mode = ...; if (S_ISDIR($mode)) { # directory ... }

      But still no joy. It simply doesn't enter the "if" block, even though I'm doing a stat on a directory. I suspect it's got something to do with the fact that the files (and dirs) are on a remote host. Anyway, reading the docs on the builtin stat has shown me how to get the permissions, i.e.

      my $ssh2 = Net::SSH2->new(); my $sftp = $ssh2->sftp(); my %fstat = $sftp->stat("${testdir}"); my $perm = $fstat{"mode"} & 07777;

      But is there no way to get the file type in the same way, i.e. using an & operator on the mode?

      Many thanks..

        yes, the S_IF* constants from Fcntl tell you the meaning of the remaining bits:
        S_IFREG => 32768 S_IFDIR => 16384 S_IFLNK => 40960 S_IFBLK => 24576 S_IFCHR => 8192 S_IFIFO => 4096 S_IFSOCK => 49152
        Regarding the compatibility of the different SSH/SFTP modules availables, read this post: Re^3: package that can handle ftp, sftp, http etc ?.
Re: Net::SSH2::SFTP stat function
by zentara (Cardinal) on Oct 01, 2008 at 19:17 UTC
    <untested>

    Can't you do a ls -la and get a list? It will tell you if something is a directory or link.

    my $chan = $ssh2->channel(); $chan->blocking(0); $chan->exec('ls -la'); while (<$chan>){ print; # regex the output here to determine what to get }

    I'm not really a human, but I play one on earth Remember How Lucky You Are
      I could do the ls -la, but I'm not supposed to use external (i.e. OS) functions in this code, only native Perl functions, to ensure portability.

      Otherwise I'd definitely use SFTP::Foreign...