Saturday, 31 December 2016

Switch statement in perl

Many programming languages offer the switch/case statement but the perl programming language does not have any default switch statement built into it. Some of the newer versions of perl however have a construct named given-when which somewhat resembles the switch statement. This is not available while coding by default & has to be enabled by via the 'use feature' statement.

The syntax for using given-when is that a given value is matched against a set of values & when a match is found the corresponding block of code gets executed. There is also a default statement which is executed in case no match is found.

given (value) {
          when (value1) { some code ; }
          when (value2) { some code ; }
          default { default result  ; }
        }

This article is describes in brief the usage of given-when construct in perl via a simple script.
Given below is the script:

[root@alive ~]# cat switch.pl
#!/usr/bin/perl -w
#

use warnings;

$|=1;
use feature qw(switch);

print "system running perl version $^V \n";

##testing switch statement##
#
my $x = 10 ;
my $y = 20 ;


print "enter value: " ;
my $z = <STDIN> ;

given($z) {
        when($x) { print "you entered the nmber $x \n" ; }
        when($y) { print "you entered the nmber $y \n" ; }
        default { print "default \n" ; }
        }


This is a simple script which first prints the version of perl running on the system. It then prompts the user to enter a value. The input is then matched against the when clauses & the corresponding code block is executed when a match is found & the default statement is executed in case no match is found. 

Note: As mentioned earlier the given-when construct is not enabled by default. To enable it I've used the statement use feature qw(switch)

Here's what happens when we execute the script.

[root@alive ~]# ./switch.pl
system running perl version v5.16.3
enter value: 10
you entered the nmber 10
[root@alive ~]# ./switch.pl
system running perl version v5.16.3
enter value: 20
you entered the nmber 20
[root@alive ~]# ./switch.pl
system running perl version v5.16.3
enter value: 30
default
[root@alive ~]#

Some common special variables in perl

Perl offers a large number of available special variable which we can use for different purposes in our scripts. This article will discus some of those variables which I've encountered & used thus far.

The $_ and @_ variable: 
The $_ and @_ variables are commonly refereed to as the default scaler & default arrary variables in perl and aptly so. When we use a loop or conditional statement or write a function, if we do not explicitly specify an input variable to the body of the loop or the conditional statement or the function then the content within the $_ or the @_ is taken as the input. These are perhaps the most commonly used special variables.

The $^O variable:
This variables tells us the OS type like linux or SunOS etc. This would help to make the scripts more portable like we can use a certain set of statements depending on the OS.

The $^V variable:
This variable contains the value of the version of perl running on the current system.

The $0 variable:
This variable contains the name of the perl script being executed.

The $ARGV/@ARGV variable:
The @ARGV variable contains the list of all the command line arguments supplied to the perl program. The scaler version would contain the individual command line argument values. The default value of ARGV scaler variable is -1 so it is advisable to change it's value to 0 when using it in scripts.

The $^I variable:
This variable is used when we want to perform some inplace changes to a file. I've used it in the script to demonstrate inplace file editing the article inplace-file-editing-in-perl.

The $| variable:
This is the output autoflush variable. It's helpful in scripts involving input being given by the user. Sometimes in programs requiring user input, the output gets buffered i.e the program waits for the user input but we do not see the prompt for it. Once we feed the input the entire program including the user input prompt gets executed in one go. Setting the value to $| to 1 helps prevent this situation.

The $. variable:
This variable contains the line number when working with files/lists.


Given below is a sample script to demonstrate the usage of the above mentioned variables. There are three more variables which I'll discus after this.

[root@alive ~]# cat 123.pl
#!/usr/bin/perl -w
#
use strict ;

hey ('one', 'two') ;

sub hey {
        print "@_ \n" ;
        }


my ($a, $b) = (10, 15) ;

print "$a and $b \n" ;

print "$^O \n" ;

print "$^V \n";


$#ARGV +=1 ;

print "$ARGV[0] \n";

print "$0 \n" ;


open (FH1, "testfile") || die "error $!" ;

while (<FH1>) { print "$. $_" ; }

$| = 1 ;

print "enter your name:" ;
my $name = <STDIN> ;

print "you entered $name \n" ;


The execution of this script yields the following output:

[root@alive ~]# ./123.pl TEST_in
one two
10 and 15
linux
v5.16.3
TEST_in
./123.pl
1 this is line 1
2 this is line 2
3 this is line 3
4 this is line 4


The last three variables I'll be discussing pertain to formatting our output.

The $\ variable:
This variable comprises of the value of the default output record separator.

The $, variable:
This variable comprises the value of the default output field separator.

The $" variable:
This variable comprises of the value of the default output field separator when we are working with double quoted string content.

Here is a sample script demonstrating the use of these three special variable:

#!/usr/bin/perl -w
#

$\="\n\n" ;
$, = "===";

open (FH, "testfile") || die ;

while (<FH>) {
my @art2 = split ;
         print "$art2[0]", "$art2[1]", "$art2[3]"  ;
 }

$" = "^-^" ;

my @ar = qw (sahil suri) ;

print "@ar" ;


The execution of the above script yields the following output:

[root@alive ~]# ./a.pl
this===is===1

this===is===2

this===is===3

this===is===4

sahil^-^suri


Thursday, 29 December 2016

Inplace file editing in perl

In this article I demonstrated an example of editing a file inplace in perl.

Consider the situation wherein we need to make some changes to a file & write them to the same file.
If we do not do anything fancy the normal way would be to use two file handles, one for input file & another for the output file.
Then loop through the input file via the input file handle, store the results in the output file handle & store the results in the output file.
Once you're done with this you can use the rename function to overwrite the original file with the new file.

Instead of doing all this we can accomplish the requirement in an easier way.

This is a script that does in place editing taking advantage of the $^I variable & the special ARGV array.

#!/usr/bin/perl -w
#
use strict ;

my $file = "testfile" ;

##setup inplace operation##
#
@ARGV = ($file) ;

##take backup of the file##
#
$^I = '.bak' ;

##doing the inplace edit##
#
while (<>) { s/test/NEW_TEST/g ; print; }


Here, we use the file called testfile containing the following lines:

[root@cent6 ~]# cat testfile
this    is         a test               file
the     second          line    test
continueing     to              third           line a test

another test I guess

The script when executed will search & replace all instances of the wrod test with NEW_TEST.


The $^I variable stores the backup of the file as the  file name followed by the .bak extension.
We assign the filename to a scaler variable named $file.Then we assign the scaler variable $file to the array variable ARGV.
In the while loop since nothing will be specified on the prompt the value of ARGV array variable will be treated as the STDIN i.e input to the while loop.
Within the body of the while loop we mention the changes we want to the file. When the loop executes the changes are written directly to the file itself eliminating the need of a temporary file.

Tuesday, 27 December 2016

Using postfix conditionals and loops in perl

Conditional & loop statements are a standard & integral part of any programming language. These type of statements follow a somewhat standard syntax. In case of a conditional statement we write a statement to have the condition checked & another statement performing an action based on the result of the condition. In case of a loop statement we first write a statement for defining the loop i.e. the conditions under which the loop will continue to iterate followed by the action performed as a result of the loop execution.

Perl offers a non-standard unique feature of defining the action to performed before mentioning the condition/loop statement. Although this is valid only when the conditional/loop block does not span more than a single statement i.e. we are performing a single action like printing the contents on an array via a foreach loop.

Here is a sample code demonstrating various usage examples for postfix conditional & loop statements along with an example for last & next statement as well.

The next statement is used to skip a match within the loop & the last statement terminates the loop when the match if found.

[root@cent6 ~]# cat post.pl
#!/usr/bin/perl -w
#
my @art = ("one", "two", "three", "four", "five") ;

print "postfix foreach example \n";
print "$_ \n" foreach (@art) ;

print "postfix if example \n";

my $a = 10;
my $b = 20;

print "true \n" if ($a < $b) ;

print "postfilx while example \n" ;

my @art2 = qw (unix linux debian) ;

print shift @art2, "\n"  while (@art2) ;

print "postfix next & last example \n" ;

my @art3 = qw (1 2 3 4 5 6 7 8) ;

print "next statement example \n" ;

foreach (@art3) {
        next if ($_ == 2) ;
        print "$_ \n" ;
        }

print "last statement example \n" ;

foreach (@art3) {
        last if ($_ == 4) ;
        print $_, "\n" ;
        }


The above script shows the following output when executed:

[root@cent6 ~]# ./post.pl
postfix foreach example
one
two
three
four
five
postfix if example
true
postfilx while example
unix
linux
debian
postfix next & last example
next statement example
1
3
4
5
6
7
8
last statement example
1
2
3
[root@cent6 ~]#


This may not sound very useful at first but it can help make your perl scripts significantly shorter when used appropriately.

Monday, 26 December 2016

Introduction to text formatting with printf

Printf is often touted as the successor to the unix echo command. This article will provide a very basic overview of how we can use print to better format the output of our shell commands.

Here's the basic syntax for printf:

printf "format string", variable1, variable2 

The format string is the key to the formatted output beacuse it specifies exactly how the formatted output should appear.
A format specifier is a special code that indicates what type of variable is displayed and how to display it

The format specifiers use the following format:

%[modifier]control-letter

The control-letter is a one-character code that indicates what type of data will be displayed and modifier defines an optional formatting feature.

Format Specifier Control Letters:

Control Letter Description
c Displays a number as an ASCII character
d Displays an integer value
i Displays an integer value (same as d)
e Displays a number in scientifi c notation
f Displays a fl oating-point value
g Displays either scientifi c notation or fl oating point, whichever is shorter
o Displays an octal value
s Displays a text string
x Displays a hexadecimal value
X Displays a hexadecimal value, but using capital letters for A through F

In addition to the control letters, you can use three modifi ers for even more control over the output:

width: This is a numeric value that specifies the minimum width of the output field. If the output is shorter, printf pads the space with spaces, using right justification for the text. If the output is longer than the specified width, it overrides
the width value.

prec: This is a numeric value that specifies the number of digits to the right of the decimal place in fl oating-point numbers, or the maximum number of characters displayed in a text string.

- (minus sign): The minus sign indicates that left justification should be used instead of right justification when placing data in the formatted space.


Here is an example of formatting a output of a text file using printf.

Consider the below file named testfile.

[root@cent6 ~]# cat testfile
this    is         a test               file
the     second          line
continueing     to              third           line

We can notice that the spacing between the strings in the file is highly irregular.
If we break the file into rows & columns we basically have three rows & four columns.

Now, let's use printf to format the output:

[root@cent6 ~]# cat testfile | awk '{printf "%-15s %-15s %-15s %-15s \n", $1, $2, $3, $4 }'
this            is              a               test
the             second          line
continueing     to              third           line


Let's take another example using some numrical data & this time we'll use printf directly without awk.
Consider the following file abc

[root@cent6 ~]# cat abc
10.345                  test
11.5894         another_test
12.4980             still_testing
13.58494044       testing

It consists of float type & string type data. If we wanted to print only upto two decimal places & ensure coulmn width to ten spaces, we may use the following printf command:

[root@cent6 ~]#  printf "%-10.2f %-10s\n" `cat abc`
10.35      test
11.59      another_test
12.50      still_testing
13.58      testing

Tuesday, 20 December 2016

installing perl modules using CPAN

The Comprehensive Perl Archive Network abbreviated as CPAN is as they themselves say "the gateway to all things perl". The site is home to thousands to perl modules going as far as 20 years & new modules are added regularly. In this short article we explore how to interact with the CPAN shell & seacrh for & install modules.

To invoke the CPAN shell type perl -MCPAN -e shell on the command line.

root@buntu:~# perl -MCPAN -e shell
Terminal does not support AddHistory.

cpan shell -- CPAN exploration and modules installation (v2.11)
Enter 'h' for help.

cpan[1]> 

At the CPAN prompt we can search for modules, get information on installed modules & install new modules.
To search for a module or a keyword we just need to type i followed by the keyword enclosed within forward slashes (//). For example, to search for SSH, just type:

cpan[1]> i /SSH/
Reading '/root/.cpan/Metadata'
  Database was generated on Sat, 10 Dec 2016 10:53:26 GMT
Fetching with LWP:
http://www.cpan.org/authors/01mailrc.txt.gz
Reading '/root/.cpan/sources/authors/01mailrc.txt.gz'
............................................................................DONE
Fetching with LWP:
http://www.cpan.org/modules/02packages.details.txt.gz
Reading '/root/.cpan/sources/modules/02packages.details.txt.gz'
  Database was generated on Wed, 14 Dec 2016 14:17:02 GMT
..............
  New CPAN.pm version (v2.14) available.
  [Currently running version is v2.11]
  You might want to try
    install CPAN
    reload cpan
  to both upgrade CPAN.pm and run the new version without leaving
  the current session.


..............................................................DONE
Fetching with LWP:
http://www.cpan.org/modules/03modlist.data.gz
Reading '/root/.cpan/sources/modules/03modlist.data.gz'
DONE
Writing /root/.cpan/Metadata
Distribution    BHEISIG/RT-Condition-NotStartedInBusinessHours-0.3.tar.gz
Distribution    BNEGRAO/Net-SSH-Expect-1.09.tar.gz
Distribution    SALVA/Net-OpenSSH-0.73.tar.gz
Distribution    SALVA/Net-OpenSSH-Compat-0.09.tar.gz
Distribution    SALVA/Net-OpenSSH-Parallel-0.14.tar.gz
Distribution    SALVA/Net-SFTP-Foreign-Backend-Net_SSH2-0.09.tar.gz
Distribution    SALVA/Net-SSH-Any-0.10.tar.gz
Distribution    SALVA/Net-SSH2-0.63.tar.gz
Distribution    SCHWIGON/Crypt-OpenSSH-ChachaPoly-0.02-reupload1.tar.gz
Distribution    SCHWIGON/Net-SSH-Perl-2.01.tar.gz
Distribution    SCOTTS/Net-SSH-W32Perl-0.05.tar.gz
Module  < Net::CLI::Interact::Transport::Net_OpenSSH (OLIVER/Net-CLI-Interact-2.200006.tar.gz)
Module  < Net::CLI::Interact::Transport::SSH (OLIVER/Net-CLI-Interact-2.200006.tar.gz)
Module  < Net::DNS::RR::SSHFP    (NLNETLABS/Net-DNS-1.06.tar.gz)
Module  < Net::Dropbear::SSH     (ATRODO/Net-Dropbear-0.10.tar.gz)
Module  < Net::Dropbear::SSHd    (ATRODO/Net-Dropbear-0.10.tar.gz)
Module  < Net::LDNS::RR::SSHFP   (CDYBED/Net-LDNS-0.75.tar.gz)
Module  < Net::Netconf::Access::ssh (JUNIPER/Net-Netconf-1.04.tar.gz)
Module  < Net::OpenSSH           (SALVA/Net-OpenSSH-0.73.tar.gz)
Module  = Net::OpenSSH::ConnectionCache (SALVA/Net-OpenSSH-0.73.tar.gz)
Module  = Net::OpenSSH::ModuleLoader (SALVA/Net-OpenSSH-0.73.tar.gz)
Module  = Net::OpenSSH::ObjectRemote (SALVA/Net-OpenSSH-0.73.tar.gz)
Module  < Net::OpenSSH::Parallel (SALVA/Net-OpenSSH-Parallel-0.14.tar.gz)
Module  < Net::OpenSSH::Parallel::Constants (SALVA/Net-OpenSSH-Parallel-0.14.tar.gz)
Module  = Net::OpenSSH::SSH      (SALVA/Net-OpenSSH-0.73.tar.gz)
Module  < Net::SFTP::Foreign::Backend::Net_SSH2 (SALVA/Net-SFTP-Foreign-Backend-Net_SSH2-0.09.tar.gz)
Module  = Net::SSH               (IVAN/Net-SSH-0.09.tar.gz)
Module  < Net::SSH2              (SALVA/Net-SSH2-0.63.tar.gz)
Module  = Net::SSH2::Channel     (SALVA/Net-SSH2-0.63.tar.gz)
Module  < Net::SSH2::Cisco       (VINSWORLD/Net-SSH2-Cisco-0.03.tar.gz)
Module  = Net::SSH2::Dir         (SALVA/Net-SSH2-0.63.tar.gz)
Module  < Net::SSH2::Expect      (JFRIED/Net-SSH2-Expect-0.2.tar.gz)
Module  = Net::SSH2::File        (SALVA/Net-SSH2-0.63.tar.gz)
Module  = Net::SSH2::KnownHosts  (SALVA/Net-SSH2-0.63.tar.gz)
Module  = Net::SSH2::Listener    (SALVA/Net-SSH2-0.63.tar.gz)
Module  = Net::SSH2::PublicKey   (SALVA/Net-SSH2-0.63.tar.gz)
Module  = Net::SSH2::SFTP        (SALVA/Net-SSH2-0.63.tar.gz)
Module  < Net::SSH::Expect       (BNEGRAO/Net-SSH-Expect-1.09.tar.gz)
Module  < Net::SSH::Perl         (SCHWIGON/Net-SSH-Perl-2.01.tar.gz)
Module  < Net::SSH::Perl::Cipher::RC4 (SCHWIGON/Net-SSH-Perl-2.01.tar.gz)
Module  < Net::SSH::Perl::Comp   (SCHWIGON/Net-SSH-Perl-2.01.tar.gz)
Module  < Net::SSH::Perl::Comp::Zlib (SCHWIGON/Net-SSH-Perl-2.01.tar.gz)
Module  < Net::SSH::Perl::Config (SCHWIGON/Net-SSH-Perl-2.01.tar.gz)
Module  < Net::SSH::Perl::Key    (SCHWIGON/Net-SSH-Perl-2.01.tar.gz)
Module  < Net::SSH::Perl::Key::DSA (SCHWIGON/Net-SSH-Perl-2.01.tar.gz)
Module  < Net::SSH::Perl::Key::Ed25519 (SCHWIGON/Net-SSH-Perl-2.01.tar.gz)
Module  < Net::SSH::Perl::Key::RSA (SCHWIGON/Net-SSH-Perl-2.01.tar.gz)
Module  < Net::SSH::Perl::Key::RSA1 (SCHWIGON/Net-SSH-Perl-2.01.tar.gz)
Module  < Net::SSH::Perl::Mac    (SCHWIGON/Net-SSH-Perl-2.01.tar.gz)
Module  < Net::SSH::Perl::Mac::MD5 (SCHWIGON/Net-SSH-Perl-2.01.tar.gz)
Module  < Net::SSH::Perl::Mac::SHA1 (SCHWIGON/Net-SSH-Perl-2.01.tar.gz)
Module  < Net::SSH::Perl::Mac::SHA2_256 (SCHWIGON/Net-SSH-Perl-2.01.tar.gz)
Module  < Net::SSH::Perl::Mac::SHA2_512 (SCHWIGON/Net-SSH-Perl-2.01.tar.gz)
Module  < Net::SSH::Perl::Packet (SCHWIGON/Net-SSH-Perl-2.01.tar.gz)
Module  < Net::SSH::Perl::ProxiedIPC (BDFOY/Net-SSH-Perl-ProxiedIPC-0.02.tar.gz)
Module  < Net::SSH::Perl::Proxy  (SCHWIGON/Net-SSH-Perl-2.01.tar.gz)
Module  < Net::SSH::Perl::SSH1   (SCHWIGON/Net-SSH-Perl-2.01.tar.gz)
Module  < Net::SSH::Perl::SSH2   (SCHWIGON/Net-SSH-Perl-2.01.tar.gz)
Module  < SSH::Batch             (AGENT/SSH-Batch-0.030.tar.gz)
Module  < SSH::Batch::ForNodes   (AGENT/SSH-Batch-0.030.tar.gz)
Module  < SSH::Command           (NRG/SSH-Command-0.07.tar.gz)
Author          BKW ("Bernhard K. Weisshuhn" <bkw@cpan.org>)
Author          BOSSHAPPY ("Jason Ungerleider" <CENSORED>)
Author          SHAW ("sshaw" <shaw@cpan.org>)
\

I've trimmed a large portion of the output for brevity. We see three distinct entities in the matches for the keyword SSH. The distribution denotes groups of related modules, the module is the actual module itself & the Author section comprises of the authors associated with the creation and publication of the modules.

To get information on an installed module we just need to type the name of the module preceeded with the i character. For example:

cpan[2]> i Net::OpenSSH
Module id = Net::OpenSSH
    CPAN_USERID  SALVA (Salvador Fandino Garcia <salva@cpan.org>)
    CPAN_VERSION 0.73
    CPAN_FILE    S/SA/SALVA/Net-OpenSSH-0.73.tar.gz
    MANPAGE      Net::OpenSSH - Perl SSH client package implemented on top of OpenSSH
    INST_FILE    /usr/share/perl5/Net/OpenSSH.pm
    INST_VERSION 0.70


To install a module, type install followed by the module name. For example

cpan[4]> install  Net::SSH::Perl::SSH2
Running install for module 'Net::SSH::Perl::SSH2'
Fetching with LWP:
http://www.cpan.org/authors/id/S/SC/SCHWIGON/Net-SSH-Perl-2.01.tar.gz
Fetching with LWP:
http://www.cpan.org/authors/id/S/SC/SCHWIGON/CHECKSUMS
Checksum for /root/.cpan/sources/authors/id/S/SC/SCHWIGON/Net-SSH-Perl-2.01.tar.gz ok
Scanning cache /root/.cpan/build for sizes
............................................................................DONE
'YAML' not installed, will not store persistent state
Configuring S/SC/SCHWIGON/Net-SSH-Perl-2.01.tar.gz with Makefile.PL
This is Net::SSH::Perl.

As of version 1.00, Net::SSH::Perl supports both the SSH1 and
SSH2 protocols natively. The two protocols have different
module prerequisitives, so you need to decide which protocol(s)
you plan to use. If you use one or the other, only those modules
for your chosen protocol will be installed; if you choose both,
all of the supporting modules will be installed. Please choose
the protocols you'd like to use from the following list ("Both"
is the default).

    [1] SSH1
    [2] SSH2
    [3] Both SSH1 and SSH2

Which protocol(s) do you plan to use? [3]

Checking for optional modules

Digest::BubbleBabble is required if you want to generate bubble babble
key fingerprints with pssh-keygen.

Would you like to install it? (y/n) [y] y


To make sure that dependent modules get installed while installing a module, type the following commands on the CPAN shell:

cpan[14]> o conf prerequisites_policy follow
    prerequisites_policy [follow]
Please use 'o conf commit' to make the config permanent!


cpan[15]> o conf commit
Unknown config variable 'commitconf'
commit: wrote '/root/.cpan/CPAN/MyConfig.pm'

You would need to exit the CPAN shell & invoke it again in order for the changes to take effect.

Sunday, 11 December 2016

Regular expressions in perl

In this article I discus working with regular expressions in perl. Less theory & more code as usual.
Regular expressions are pieces/parts of a string resembling a particular pattern being searched in a file.
In perl, strings enclosed within backslahes ( //) are considered as input regular expression matching & the =~ is used to indicate that the expression to the right is a regular expression match. The !~ is used to indicate a negative regex match. More on that towards the end of the article.


Pattern matching:
In the first example, we open a text file via a file handle & assign it to the array & then iterate through the file in a foreach loop & print any line containing the word test in it.

This is the test file:

root@buntu:~# cat testfile
this is a test
still a test

a regex test

using regex test in arrays

c:\documnets and settings
c:\downloads

c:\program files

d:\tmpfiles

SahiLSuri708


This is the script:

root@buntu:~# cat reg1.pl
#!/usr/bin/perl -w

open (FH1, "testfile") || die "can't open file" ;

my @art = <FH1> ;

foreach (@art) {
        if (/test/) { print }
        }

This is the output from the script:

root@buntu:~# ./reg1.pl
this is a test
still a test
a regex test
using regex test in arrays


Search & replace:
With the basic pattern match illustrated in the above example, in this next example I demonstrate searching for a pattern & replacing it with something else.

root@buntu:~# cat regex.pl
#!/usr/bin/perl -w

my $my_name = "sahil suri" ;

$my_name =~ s/^sahil/Sahil/ig ;
$my_name =~ s/suri$/Suri/ig ;

print "$my_name \n" ;


open (FH1, "testfile") || die "can't open file" ;

my @art = <FH1> ;

foreach (@art) {
        s/test\s/Perl Test /g ;
        print "$_ \n" ;
        }

In the first part of the script, I performed a search and replace in the value of the scaler variable my_name. Here, the s implies the search, i states that search should be case insensitive and g indicates a global replacement of the matched string. If we do not mention g then only the first occurance of the string is replaced. The ^ and $ are called anchor tags. The ^ symbol indicates the beginning of the sentence and the $ indicates the end of a sentence.
In the second part of the script, I do a search & replace in an array variable via a foreach loop. So, each line of the file testfile is checked for the existance of the string test.followed by a space indicated by \s. If a match is found then the match is replace by the string Perl Test and is printed via the print statement that follows.


Using grouping:
Grouping allows us to match regular expressions & assign the matched values to variables $1, $2 and so on. Here are a couple of example:

my $os = "Solaris 1 2 3 4" ;
$os =~ /(\s\d)(\s\d)(\s\d)(\s\d)/ ;

print "$1 $2 $3 $4 \n" ;

my $time = `date +%r` ;

$time =~ /(\d\d):(\d\d):(\d\d)\s(.*)/ ;

print "$1 $2 $4 \n" ;

In order to group selections we enclose them in parenthesis i.e (). The \d indicates a single digit, \s indicates a space character & .* indicates all characters. In the first example, the four digits get selected & their values get assigned to $1, $2, $3 & $4 respectively. After that we print them. 
In the second example, we select the hours, minutes, seconds & AM/PM from the date command & print the hours, minutes & AM/PM respectively.

We can also use grouping to match alphanumeric strings like Sahil777. Given below is an example of performing such a match.

open (FH1, "testfile") || die " couldn't open file" ;

my @abc = <FH1> ;

foreach (@abc) {
        if (/(.*)(\d)/) { print ; }
        }


Character classes:
This type of regex matching is helpfule when we want to match a character or a group of characters from the string. Here's an exmaple:

my @ays = ("may", "say", "hay", "yay", "hurray", "whey" ) ;

foreach (@ays) {
        if (/[mshyr]ay/i)
                {
                  print "$_ \n" ;
                }
        }

The regex match /[mshyr]ay/i matches any words comprising of the characters m or s or h or y or r followed by the characters ay.


Alternates:
These are loosely equivallent to an OR match i.e. if any of the alternates match, perform the required actions on them as intented in the script. Here's an exmaple:

my @ors = ("solaris", "linux", "hp-ux", "aix", "debian", "ubuntu") ;

foreach (@ors) { if (/solaris|linux|aix/) { print "$_ \n" } }

The above example looks for the strings solaris or linux or aix in the array ors and prints them if nay match is found.

In addition to the match shown above, we can also group alternates as shown in the following example:

foreach (@ors) { if (/(solaris|linux|aix)/) { print "$1 \n" } }


Ommiting special characters and blank lines:
To discard the special meaning of a character we use the backslah to escape it. Here's an example:

open (FH2, "testfile") ;
my @art2 = <FH2> ;

foreach (@art2) {
        if (/(.:)(.*\\)(.*)/) {
                print "$1$2$3 \n" ;
        }
}

The file testfile as I printed in the beginning of the article has a couple of windows type path names like c:\documnets and settings. The above sample code will interpret the backslash in the path name as a literal character & print it out.
To remove empty lines from the file before printing it use . character which will match to lines having atleast one character & skip empty lines:

open (FH1, "testfile") || die " couldn't open file" ;

my @art2 = <FH1> ;

foreach (@art2) { if (/./) { print $_ ; } }


Negative matches:
We may often find ourselves in a situation wherein we want to negate certain pattern from a file & print the rest of the file. We can accomplish this via a couple of methods. 
The first one is using something called a negative lockhead which is the ?! character. Given below is an example:

open (FH1, "testfile") || die " couldn't open file" ;

my @abc = <FH1> ;

foreach (@abc) {
        if (/^(?!c:)/ and /./) {
                print ;
        }
}

This code prints every line from the file that does not begin with c: and is not empty.

The second method to do a negative regex match is using !~ as illustrated in the below example:

open (FH1, "testfile") || die " couldn't open file" ;

my @abc = <FH1> ;

foreach (@abc) {
        #if ($_ !~ /[:]/ && /./) { print }
        if ($_ !~ /(test)/ && /./) { print }
        }

The above code skips any lines that contain the word test and any lines that are empty. The commented out if statement skips any lines that contain a colon and any empty lines.

Saturday, 10 December 2016

Common directory operations in perl


This article is about common directory related operations that we'd like to perform in perl. This comprises of creating a directory, creating a directory path similar to mkdir -p, opening directories & deleting directories. I'll conclude the article with a mention of the find file function which is somewhat the find equivallent of the unix find command.

Like earlier articles this article will also illustrate the directory related functions available in perl via a single script which is as follows:

root@buntu:~# cat dirop.pl
#!/usr/bin/perl

use File::Path;
use File::Spec;

my $mydir = "/root" ;

##open a directory##

opendir (dirh, "$mydir") || die "couldn't open directory" ;

my @rdir = readdir(dirh) ;

foreach (@rdir) { print "$_ \n" ; }
closedir (dirh) ;

#my @sdir = `ls -la /root` ;
#foreach (@sdir) {
#my @rd =  split /\s+/ ;
#print "$rd[8] \n" ;
#}

##directory creation ##

my $newdir = "/test" ;
my $testdir = "/test1" ;

mkdir $newdir || die "couldn't create directory" ;

mkdir $testdir || die " couldn't create directory" ;

system ("ls -ld $testdir") ;

rmdir $testdir ;

mkpath ('/test/abc/def/ghi');

mkpath ('/test1/abc/def/ghi') ;

system ("ls -lR $testdir") ;

##directory deletion example similar to rm -r in UNIX##

rmtree ('/test1/abc/def/ghi') ;

##change a directory in a running script##

chdir "/tmp" ;

system ("pwd") ;


The execution of this code yields the following output:

root@buntu:~# ./dirop.pl
file2op.pl
hostlist
.
oldfile_copy
test4.pl
.viminfo
test9.pl
.bash_history
.lesshst
result
test5.pl
.bashrc
.cpan
oldfile.bkp
afile
dirop.pl
test3.pl
.nano
test6.pl
..
.profile
find.pl
test2.pl
outfile.txt
newfile
.cpanm
abc.pl
select.sh
test.pl
passwd
.libnet-openssh-perl
testfile
map.pl
testfile_soft
.ssh
testfile_hard
fileop.pl
test7.pl
drwxr-xr-x 3 root root 4096 Dec 10 12:57 /test1
/test1:
total 4
drwxr-xr-x 3 root root 4096 Dec 10 12:57 abc

/test1/abc:
total 4
drwxr-xr-x 3 root root 4096 Dec 10 22:10 def

/test1/abc/def:
total 4
drwxr-xr-x 2 root root 4096 Dec 10 22:10 ghi

/test1/abc/def/ghi:
total 0
/tmp
root@buntu:~#


Last but not the least I'll talk about the find function. We can use the find function in perl to seacrh for files/directories based on certain attributes such as modified date, size & also regular expression matches.

The below example script searches for any files ending with .txt or .sh in the /root directory which have been modified less than two days ago.

root@buntu:~# cat find.pl
#!/usr/bin/perl -w

use File::Find;

my @files;
my @dirpath=qw(/root);
find(sub {
           push @files,$File::Find::name if (-f $File::Find::name and /\.txt|sh$/ and int(-M $_) < 4);
      }, @dirpath);


print join "\n",@files, "\n";

The following is the output of the script:

root@buntu:~# ./find.pl
/root/select.sh
/root/.cpan/sources/authors/01mailrc.txt.gz
/root/.cpan/sources/modules/02packages.details.txt.gz

Using select to create menu driven shell scripts


Menu driven scripts are an important aspect to consider when writting large scripts which would require user input for further processing. We can use an infinite while loop to create a menu for a shell script & that is quite common. If you have an X windows system available then you can make use of dialog or whiptail to create nice menus & dialog boxes for your scripts. In this article I'll domonstrate how we can use the select command for creating simple menu driven scripts.

The syntax for select command is:

select var in list
do
    commands..
done

The select command runs an inifinite loop. var is the variable you'd use in your case statement for the menu. The list would be the menu options. The commands would be part of the case statements being issued during the execution of the menu selection.
We can write the commands to be executed for a menu selection within the select loop but for a cleaner code I'll write the commands within separate functions & just call the functions in the case statements within the select loop.

So, here is the example script:

root@buntu:~# cat select.sh
#!/bin/bash

trap '' 2  # ignore ctrl+c

##set PS3 prompt##
PS3="Enter your selection: "

function diskspace {
        df -h
}

function users {
        who
}

select opts in "check disk usage" "check users" "exit script"
do
        case $opts in
                "check disk usage")
                                echo -e "\e[0;32m $(diskspace) \e[m"
                                ;;
                "check users")
                                echo -e "\e[0;32m $(users) \e[m"
                                ;;
                "exit script")
                                break
                                ;;
                *)
                                 echo "invalid option"
                                ;;
        esac
done


The script when executed will present three options to the user. Selecting option 1 will call the diskspace function, option 2 will the users function & option 3 will exit out of the loop.
I've added a trap to ignore ctrl+c presses or the INT signal & added some color to the output. 


Using capture groups in grep in Linux

Introduction Let me start by saying that this article isn't about capture groups in grep per se. What we are going to do here with gr...