in reply to scp output without having to use "Net::SCP" in Perl

Hi, The below code works fine.

chdir '/user/perl_monks' || die "cannot change dir: $!"; open(my $fh, "ls|") || die "cannot open the file handle:$!"; my @files = <$fh>; chomp @files; for ( @files) { system ( "scp $_ username\@host:/any/folder/" ); }

I used file handle to extract the contents and remove the newline char. I also found that special characters like @ inside the system qq needs to be escaped.

Replies are listed 'Best First'.
Re^2: scp output without having to use "Net::SCP" in Perl
by afoken (Chancellor) on Jun 05, 2015 at 11:09 UTC

    Hi, The below code works fine.

    No, it does not. It actually is dangerously wrong.

    chdir '/user/perl_monks' || die "cannot change dir: $!"; open(my $fh, "ls|") || die "cannot open the file handle:$!"; my @files = <$fh>; chomp @files; for ( @files) { system ( "scp $_ username\@host:/any/folder/" ); }

    Here is what's wrong with this code:

    1. Newlines are legal in filenames. So, ls may return a single filename splitted over two or more lines, resulting in a filename splitted over two or more elements of @files.
    2. At least GNU ls behaves differently depending on environment variables. It may quote and/or escape special characters in filenames (see GNU ls documentation), but your code assumes sane defaults.
    3. Various characters that have a special meaning to the shell are legal in filenames. (In fact, all characters except ASCII NUL and / are legal in filenames.) So you may legally name a file $(rm -rf /) or $(rm -rf *). Because your code use the single-argument form of system, and it does not clean up $_ before interpolation, the shell will be invoked, and it will interpret those filenames according to shell rules. So, with these filenames, you will likely delete a lot of files.
    4. You blindly assume that system and scp succeed.
    5. Shells are different. Quoting rules change from shell to shell, so there is no safe way to handle the shell.

    And here is how to fix it:

    1. Don't use the shells to read filenames from a directory. glob will do, as will opendir, readdir, closedir, and IO::Dir. For a simple "ls" with sane defaults, glob('*') should do the job.
    2. See above. Avoiding ls also avoids all quoting and escaping issues of ls.
    3. Use the multiple-argument form of system, i.e. system('/path/to/scp',$_,'username@host:/any/folder');.
    4. Check the return value of system to see if system or scp failed. Also look at $!.
    5. Avoid the shells. It just makes life harder. If you want a harder life, read http://www.in-ulm.de/~mascheck/various/.
    I also found that special characters like @ inside the system qq needs to be escaped.

    This has nothing to do with system, you always have to escape @ inside double quoted strings.

    Also note that the qx// operator, also in the backticks form, can also be used in list context. There, it returns one list element per line of output. (This is documented in I/O Operators.) You don't have to use pipe-open and readline (<$handle>), chomp(my @files=`ls`) would be sufficient. But see above for why this is at least problematic, if not dangerous.

    Alexander

    --
    Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)