CleverDev
CleverDev

Reputation: 487

How to use split function in perl to do the work of awk

I am writing a script in perl and I want to avoid using external commands as awk, tr , etc as much as possible. This is the command I am running from inside the script , using backticks:

my @arr = `$cmd | tr " " "\n" | xargs -n1 host | awk '{print $4}'`

And this is the output with out awk '{print 4$} :

$fileserver has address 10.90.207.59
$fileserver has address 10.90.207.34
$fileserver has address 10.90.207.56
$fileserver has address 10.90.207.67

This is the output with awk '{print 4$}:

10.90.207.59
10.90.207.34
10.90.207.56
10.90.207.67

I want each IP to be in a separated cell in the array. How can I use perl functions (maybe split or map) currectrly instead of using awk and tr?

Upvotes: 2

Views: 905

Answers (5)

Yordan Georgiev
Yordan Georgiev

Reputation: 5430

perl oneliners to the rescue - list my taxi receipts, sort by the 4th col, with _ token delim:

    ls -1 | sort -t'_' -k 4| grep -i taxi
    00016,70_2021-12-04_OL_2021-12-04_id-10_LahiTaxi-Oy_TaxiKuljetus.jpg
    00125,10_2021-12-04_OL_2021-12-04_id-11_Tom-Lindroos_Taxipalvelut.jpg

seem them as a table (for each row in the console, grab into the @a array ( this could be even shorter ... ) , than refer to each element of the array by it's index:

ls -1 | sort -t'_' -k 4|perl -ne 'chomp($_);@a=split/_/;print "$a[4]\t$a[3]\t$a[2]\t$a[1]\t$a[5]\t$a[6]\t$a[0]\n"' | grep -i taxi

    id-10   2021-12-04      OL      2021-12-04      LahiTaxi-Oy     TaxiKuljetus.jpg        00016,70
    id-11   2021-12-04      OL      2021-12-04      Tom-Lindroos    Taxipalvelut.jpg        00125,10

put them into a csv file, to import into xls, google sheet:

  ls -1 | sort -t'_' -k 4|perl -ne 'chomp($_);@a=split/_/;print "$a[4];$a[3];$a[2];$a[1];$a[5];$a[6];$a[0]\n"' > ../lst.csv

Upvotes: 0

brian d foy
brian d foy

Reputation: 132858

If you know that the field that you want is the last column, you can use an index that counts backward from the end:

my $ip = (split)[-1];

In the one-liner case, using the -a switch makes Perl split the line into the @F array (the -n wraps while(<>){...} around the argument to -e and the -l adds a newline to each print (see perlrun)):

perl -anle 'print $F[-1]'

However, since you're not stuck with awk, you don't have to do it that way. You can probably do most of that in Perl without the pipeline. Here's what you started with:

my @arr = `$cmd | tr " " "\n" | xargs -n1 host | awk '{print $4}'`

It looks like $cmd generates a bunch of host names in a single line. You turn spaces into newlines with the tr, then run host on each of lines. For each host output, you grab the address.

You could do that in a single Perl program:

 use v5.24;

 use Socket;        # core module
 use Net::hostent;  # core module

 my $cmd = ...;
 foreach my $host ( `$cmd` ) {
      chomp( $host );
      my @addresses = 
          map { inet_ntoa($_) } 
          gethostbyname($host)->addr_list->@*;
      say join "\n", @addresses;
     }

With the backticks in a list context, Perl breaks the output of the command into lines for you. The core modules Socket and Net::hostent come with Perl.

I've used v5.24 for its very nice postfix dereferencing feature, ->@*, that turns the array reference from addr_list into a regular list that map can use.

You need to be careful about whatever you put in $cmd. I talk about that at length in the "Security" chapter of Mastering Perl. You can also find some stuff in the perlsec documentation.

Upvotes: 3

Borodin
Borodin

Reputation: 126742

With the help of the inet_aton and inet_ntoa from the Socket module, you can very simply do the entire thing in Perl, with the exception of $cmd because you don't tell us what that is

It looks like your $cmd returns one or more lines of host names, separated by spaces, so I've used an echo command to emulate it here. I've also used Data::Dump just to reveal the final contents of @addresses

use strict;
use warnings 'all';

use Socket;

my $cmd = 'echo www.amazon.co.uk www.perl.com www.stackoverflow.com';

my $cmd_output = `$cmd`;
my @addresses = map { name_to_ip($_) } split ' ', $cmd_output;

use Data::Dump;
dd \@addresses;

sub name_to_ip {

    my ($name) = @_;
    my $add32 = inet_aton($name) or die qq{Unable to convert host name "$name": $!\n};

    inet_ntoa($add32);
}

output

["54.239.36.155", "207.171.7.72", "151.101.193.69"]

Upvotes: 2

mkHun
mkHun

Reputation: 5927

For similar use perl one liner

perl -nae 'print "$F[3]\n"' input.txt

-n make the loop for a file

-a autosplit mode. By default split with space. The output stored into the @F. So I use third index to print the ip address. In perl index start with 0

Upvotes: 5

Sobrique
Sobrique

Reputation: 53488

Trivially. By default split works just like awk. So:

my @arr = split;
print $arr[3]; #note - arrays start at zero. 

However, perl generally also works line by line on a filehandle, and a split gets you stuff you don't want.

You can either:

#!/usr/bin/perl
use strict;
use warnings;

while (<DATA>) {
   my ($ip) = (split)[3];
   print $ip, "\n";
   #or push it. 
}

__DATA__
$fileserver has address 10.90.207.59
$fileserver has address 10.90.207.34
$fileserver has address 10.90.207.56
$fileserver has address 10.90.207.67

But if you're looking for a single thingummy:

#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;

my @ips = map { (split)[3] } <DATA>;

print Dumper \@ips;


__DATA__
$fileserver has address 10.90.207.59
$fileserver has address 10.90.207.34
$fileserver has address 10.90.207.56
$fileserver has address 10.90.207.67

We read <DATA> in a list context, so it returns the whole thing - one element at a time to map.

And then in the map we split each, and grab element 3 ($4 in awk terms).

Upvotes: 5

Related Questions