Reading output from command into Perl array - perl

I want to get the output of a command into an array — like this:
my #output = `$cmd`;
but it seems that the output from the command does not go into the #output array.
Any idea where it does go?

This simple script works for me:
#!/usr/bin/env perl
use strict;
use warnings;
my $cmd = "ls";
my #output = `$cmd`;
chomp #output;
foreach my $line (#output)
{
print "<<$line>>\n";
}
It produced the output (except for the triple dots):
$ perl xx.pl
<<args>>
<<args.c>>
<<args.dSYM>>
<<atob.c>>
<<bp.pl>>
...
<<schwartz.pl>>
<<timer.c>>
<<timer.h>>
<<utf8reader.c>>
<<xx.pl>>
$
The output of command is split on line boundaries (by default, in list context). The chomp deletes the newlines in the array elements.

The (standard) output does go to that array:
david#cyberman:listing # cat > demo.pl
#!/usr/bin/perl
use strict;
use warnings;
use v5.14;
use Data::Dump qw/ddx/;
my #output = `ls -lh`;
ddx \#output;
david#cyberman:listing # touch a b c d
david#cyberman:listing # perl demo.pl
# demo.pl:8: [
# "total 8\n",
# "-rw-r--r-- 1 david staff 0B 5 Jun 12:15 a\n",
# "-rw-r--r-- 1 david staff 0B 5 Jun 12:15 b\n",
# "-rw-r--r-- 1 david staff 0B 5 Jun 12:15 c\n",
# "-rw-r--r-- 1 david staff 0B 5 Jun 12:15 d\n",
# "-rw-r--r-- 1 david staff 115B 5 Jun 12:15 demo.pl\n",
# ]

Enable automatic error checks:
require IPC::System::Simple;
use autodie qw(:all);
⋮
my #output = `$cmd`;

Related

How to capture large STDOUT output in Perl when executing an external command

I want to get the list of file names present in the remote location.
I am using the below snippet in my Perl script.
my $command = "sftp -q -o${transferAuthMode}=yes -oPort=$sftpPort ${remoteUsername}\#${remoteHost} 2>\&1 <<EOF\n" .
"cd \"${remotePath}\"\n" .
"ls -l \n" .
"quit\n" .
"EOF\n";
my #files = `$command`;
When the number of files in the remote location is large (>500) then not all the file names are captured in #files.
When I manually do SFTP and list the files, all files are getting listed but I'm not getting the same through the script. Each time getting #files size different. It's occurring only when there are large number of files.
I'm unable find the reason behind this. Could you please help?
This can be achieved without requiring any additional package module/s. I tested this on my CentOS 7 Server (Windows VM).
My remote host details: I got ~2000 files in the remote host dir. A CentOS 6.8 server.
%_gaurav#[remotehost]:/home/gaurav/files/test> ls -lrth|head -3;echo;ls -lrth|tail -2
total 7.9M
-rw-rw-r--. 1 gaurav gaurav 35 Feb 16 23:51 File-0.txt
-rw-rw-r--. 1 gaurav gaurav 35 Feb 16 23:51 File-1.txt
-rw-rw-r--. 1 gaurav gaurav 38 Feb 16 23:51 File-1998.txt
-rw-rw-r--. 1 gaurav gaurav 38 Feb 16 23:51 File-1999.txt
%_gaurav#[remotehost]: /home/gaurav/files/test>
Script output from LocalHost: Please note that I am running your command sans the o${transferAuthMode}=yes part. As seen below, the script is able to gather all results in an array, greater than 500 results.
I am prnting the total entries, some particular index numbers from the array to show the results, but give it a try with un-commented Dumper line to see the full result.
%_STATION#gaurav * /root/ga/study/pl> ./scp.pl
Read 2003 lines from SCP command.
ArrayIndex: 2,3,1999,2000 contain:
[-rw-rw-r-- 0 501 501 36B Feb 16 23:51 File-58.txt]
[-rw-rw-r-- 0 501 501 37B Feb 16 23:51 File-129.txt]
[-rw-rw-r-- 0 501 501 38B Feb 16 23:51 File-1759.txt]
[-rw-rw-r-- 0 501 501 38B Feb 16 23:51 File-1810.txt]
%_STATION#gaurav * /root/ga/study/pl>
Script and its Working:
#!/usr/bin/perl
use strict ;
use warnings ;
use Data::Dumper ;
my $sftp_port=22 ;
my ($user, $host) = ("gaurav","192.168.246.137") ;
my $remote_path = '/home/gaurav/files/test' ;
my #result ; # To store result
my $command = "sftp -q -oPort=$sftp_port ${user}\#${host} 2>\&1 <<EOF\n"."cd $remote_path\nls -lrth\nquit\nEOF" ;
# open the command as a file handle, read output and store it.
open FH, "$command |" or die "Something went wrong!!\n" ;
while (<FH>) {
tr/(?\r|\f|\n)//d ; # Removing any new line, carriage return or form feed.
push(#result,"\[$_\]") ;
}
close FH ;
#print Dumper #result ;
# Just for printing a little bit of results from
# the array. Following lines can be deleted.
my $total = scalar #result ;
print "Read $total lines from SCP command.\n" ;
print "\nArrayIndex: 2,3,1999,2000 contain:\n
$result[2]
$result[3]
$result[1999]
$result[2000]
" ;
Another way: One could also get around this issue by making a shell script and calling it from the perl script and read its output. As shown below, my shell script which gets called by the perl script and the final output. This can be used as a quick technique when one doesn't have much time to write/formulate commands in perl directly. You can use the qx style(shown below) in earlier script as well.
Shell script "scp.sh"
%_STATION#gaurav * /root/ga/study/pl> cat scp.sh
#!/bin/bash
sftp -oPort=${1} ${2}#${3} 2>&1 <<EOF
cd ${4}
ls -l
quit
EOF
Perl Script "2scp.pl"
%_STATION#gaurav * /root/ga/study/pl> cat 2scp.pl
#!/usr/bin/perl
use strict ;
use warnings ;
use Data::Dumper ;
my $sftp_port=22 ;
my ($user, $host) = ("gaurav","192.168.246.137") ;
my $remote_path = '/home/gaurav/files/test' ;
# Passing arguements to shell script using concatination.
my $command = './scp.sh '." $sftp_port $user $host $remote_path" ;
my #result = qx{$command} ; # Runs the command and stores the result.
my $total = scalar #result ;
print "Read $total lines from SCP command.\n" ;
# End.
Output:
%_STATION#gaurav * /root/ga/study/pl> ./2scp.pl
Read 2004 lines from SCP command.
%_STATION#gaurav * /root/ga/study/pl>
Try it out and let us know.
Thanks.

Creating filenames with unicode characters

I am looking for some guidelines for how to create filenames with Unicode characters. Consider:
use open qw( :std :utf8 );
use strict;
use utf8;
use warnings;
use Data::Dump;
use Encode qw(encode);
my $utf8_file_name1 = encode('UTF-8', 'æ1', Encode::FB_CROAK | Encode::LEAVE_SRC);
my $utf8_file_name2 = 'æ2';
dd $utf8_file_name1;
dd $utf8_file_name2;
qx{touch $utf8_file_name1};
qx{touch $utf8_file_name2};
print (qx{ls æ*});
The output is:
"\xC3\xA61"
"\xE62"
æ1
æ2
Why doesn't it matter if I encode the filename in UTF8 or not? (The filename still becomes valid UTF8 either way.)
Because of a bug called "The Unicode Bug". The equivalent of the following is happening:
use Encode qw( encode_utf8 is_utf8 );
my $bytes = is_utf8($str) ? encode_utf8($str) : $str;
is_utf8 checks which of two string storage format is used by the scalar. This is an internal implementation detail you should never have to worry about, except for The Unicode Bug.
Your program works because encode always returns a string for which is_utf8 returns false, and use utf8; always returns a string for which is_utf8 returns true if the string contains non-ASCII characters.
If you don't encode as you should, you will sometimes get the wrong result. For example, if you had used "\x{E6}2" instead of 'æ2', you would have gotten a different file name even though the strings have the same length and the same characters.
$ dir
total 0
$ perl -wE'
use utf8;
$fu="æ";
$fd="\x{E6}";
say sprintf "%vX", $_ for $fu, $fd;
say $fu eq $fd ? "eq" : "ne";
system("touch", $_) for "u".$fu, "d".$fd
'
E6
E6
eq
$ dir
total 0
-rw------- 1 ikegami ikegami 0 Jul 12 12:18 uæ
-rw------- 1 ikegami ikegami 0 Jul 12 12:18 d?

Perl print changes order of execution

I got a pretty strange problem with my perl-script. I really have no idea, what's happening there, maybe you will see it...
My script uses Net::SSH::Perl to connect to a host-machine via SSH. After that I head -20 on a config.ini and search for some pattern (a version number).
After I print the version number i cat some other file which is approximately 700 lines long.
What's happening now is, that instead of printing the version-number, the script prints the whole 700-line-file. Unless I addprint "\n"x10; directly behind the version-print.
This behaviour doesn't make any sense to me, but I'm sure one of you will be smarter than me and know the answer :)
Below the script:
1 #!/usr/local/bin/perl -w
2 use lib './';
3 use strict;
4
5 use GetVersions;
6 use Net::SSH::Perl;
7
8
9 my $ssh = Net::SSH::Perl->new(
10 'host',
11 identity_files => ['~/.ssh/id_rsa']
12 );
13
14 $ssh->login('user');
15
16 my $ver_regex = '^psadm\.version\s*=\s*rel_(.+)$';
17
18 my ($file, $err, $exit) = $ssh->cmd('head -20 /config.ini');
19 my $version = $file =~ /$ver_regex/m;
20 print "version: $version\n",
21 #print "\n"x10;
22 ($file, $err, $exit) = $ssh->cmd('cat /application.ini');
This should probably be a semicolon
print "version: $version\n",
^

How to extract the column data from multidimensional array in perl

ls -l
-rw-r--r-- 1 angus angus 0 2013-08-16 01:33 copy.pl
-rw-r--r-- 1 angus angus 1931 2013-08-16 08:27 copy.txt
-rw-r--r-- 1 angus angus 492 2013-08-16 03:15 ex.txt
-rw-r--r-- 1 angus angus 25 2013-08-16 09:07 hello.txt
-rw-r--r-- 1 angus angus 98 2013-08-16 09:05 hi.txt
I need only the read, write , access data as well as the file name.
#! /usr/bin/perl -w
#list = `ls -l`;
$index = 0;
#print "#list\n";
for(#list){
($access) = split(/[\s+]/,$_);
print "$access\n";
($data) = split(/pl+/,$_);
print "$data";
#array1 = ($data,$access);
}
print "#array1\n"
I have written this code to extract the read,write,access permission details and the file name corresponding to it.
I couldn't extract the filename which is the last column.
Check perl stat http://perldoc.perl.org/functions/stat.html
It's more robust and efficient than calling external ls command,
use File::stat;
$sb = stat($filename);
printf "File is %s, size is %s, perm %04o, mtime %s\n",
$filename, $sb->size, $sb->mode & 07777,
scalar localtime $sb->mtime;
I think you have an error in line number 8 of your script. You are trying to split the line using the string "pl" as a delimiter which will only match the first line of your input and will not give you what I think you want.
I believe you should just split the whole line on white space and assign just the columns you want (number 1 and 8 in this case).
change your loop for this:
for my $filename (#list){
chomp($filename);
my ($access, $data) = (split(/\s+/, $filename))[0, 7]; #use a slice to get only the columns you want.
print "$access $data\n";
}
Note: mpapec suggestion to use Stat would be better. I just wanted to let you know why your code is not working.

How to extract a particular column of data in Perl?

I have some data from a unix commandline call
1 ab 45 1234
2 abc 5
4 yy 999 2
3 987 11
I'll use the system() function for the call.
How can I extract the second column of data into an array in Perl? Also, the array size has to be dependent on the number of rows that I have (it will not necessarily be 4).
I want the array to have ("ab", "abc", "yy", 987).
use strict;
use warnings;
my $data = "1 ab 45 1234
2 abc 5
2 abc 5
2 abc 5
4 yy 999 2
3 987 11";
my #second_col = map { (split)[1] } split /\n/, $data;
To get unique values, see perlfaq4. Here's part of the answer provided there:
my %seen;
my #unique = grep { ! $seen{ $_ }++ } #second_col;
You can chain a Perl cmd-line call (aka: one-liner) to your unix script:
perl -lane 'print $F[1]' data.dat
instead of data.dat, use a pipe from your command line tool
cat data.dat | perl -lane 'print $F[1]'
Addendum:
The extension for unique-ness of the resulting column is straightforward:
cat data.dat | perl -lane 'print $F[1] unless $seen{$F[1]}++'
or, if you are lazy (employing %_):
cat data.dat | perl -lane 'print unless $_{$_=$F[1]}++'