📜 ⬆️ ⬇️

Perl One-Line Programs

Introduction


I'm going to talk about Perl single-line programs. If you master one-line Perl, you can save a lot of time (I save).

The purpose of the post is to show how Perl can be used instead of find, grep, awk, sed. At the end of the post it will be written why it is necessary.

Well, first things first.

Flags


-E flag
The flag allows you to run barley code directly in the console, I use this feature to check some test code.
Suppose I want to know the decimal value of the hexadecimal number 0xFA23B:
perl -e "print 0xFA23B" 

Note. When the single-line barley code is launched from under Windows, the code should be enclosed in double quotes:
 perl -e "print 0xFA23B" 
, in the case of Linux / Unix, the code can be either in double quotes or in single quotes, but in Unix / Linux, the case of double quotes has to be escaped with "$" signs:
 perl -e "\$i = 0;print \$i" 

Note. After the -e flag should immediately follow the code .
')
-L flag
This flag makes initializes the variables $ / and $ \ value "\ n";
The variable $ / sets the separator of the input fields.
The variable $ \ specifies what will be output after the print command.

Program:
 perl -le "print 1" 

Equivalent to the following:
 BEGIN { $/ = "\n"; $\ = "\n"; } print 1; 

Thus, in the end, you do not have to write print "\ n";

-N flag
From here begins the most interesting.

The following code:
 perl -ne 'print 1' 

equivalent to this:
 LINE: while (defined($_ = <ARGV>)) { print 1; } 

Where can this be used?
And here, for example, we need to add to the names of files whose names begin with numbers, the extension “bak”:

Voila:
 ls | perl -lne 'rename $_, "$_.bak" if /^\d+/' 

And for Windows? You are welcome:
 dir /b | perl -lne "rename $_, \"$_.bak\" if /^\d+/" 

Let's look at the resulting program:
 BEGIN { $/ = "\n"; $\ = "\n"; } LINE: while (defined($_ = <ARGV>)) { chomp $_; rename $_, "$_.bak" if /^\d+/; } 

chomp $ _; took from the -l flag: together with -n, it also adds chomp $ _ ;, and not just BEGIN {$ / = "\ n"; $ \ = "\ n"; }

-A flag
The -a flag allows perl to be used as awk.

The following code:
 perl -nae "print 1" 

equivalent to:
 LINE: while (defined($_ = <ARGV>)) { our(@F) = split(" ", $_, 0); print 1; } 

That is, each line is split by a split by spaces, and the resulting fields are put into an @F array.
The field separator can be changed using the -F flag.

Suppose you need to output usernames with their home directories from the / etc / passwd file:
 less /etc/passwd | perl -F: -nlae 'print "$F[0]:$F[4]"' 

And for Windows, for example, I want to know the names of the files in the folder that I last changed in September 2009:
 dir /TW | perl -nale "print $F[$#F] if $F[0] =~ /\.09\.2009/" 

-P flag
This flag does the same, that -n only adds another block continue c "print $ _".

The following code:
 perl -pe "print 1" 

equivalent to:
 LINE: while (defined($_ = <ARGV>)) { print 1; } continue { print $_; } 

Suppose we output the file / etc / passwd, simultaneously replacing 3 with 6.

Instead of this code:
 less /etc/passwd | perl -ne "s/3/6/;print \$_" 

We can write:
 less /etc/passwd | perl -pe "s/3/6/" 

-I flag
The i flag allows you to change files.

Next program:
 perl -i.bak -pe "s/foo/bar/" 

equivalent to this:
 BEGIN { $^I = ".bak"; } LINE: while (defined($_ = <ARGV>)) { s/foo/bar/; } continue { print $_; } 

which in turn is equivalent to this:
 $extension = '.bak'; LINE: while (<>) { if ($ARGV ne $oldargv) { if ($extension !~ /\*/) { $backup = $ARGV . $extension; } else { ($backup = $extension) =~ s/\*/$ARGV/g; } rename($ARGV, $backup); open(ARGVOUT, ">$ARGV"); select(ARGVOUT); $oldargv = $ARGV; } s/foo/bar/; } continue { print; # this prints to original filename } select(STDOUT); 

In brief, I’ll explain what happens when the perl -i.bak -pe “code” <file name> lines are called. For example, we call:
 perl -i.bak -pe "s/foo/bar/" test.txt 

The test.txt file is renamed to the test.txt.bak file, and a new test.txt file is created. Then, in each line of the source file, foo is replaced with bar, which are written to the new test.txt file (apparently, although the file was renamed, do we still have access to its strings?)

Suppose you need to replace \ r \ n with \ n in the file:
 perl -i.bak -pe 's/\r\n/\n/' test.txt 

As a result of this code, you will get two files: one is test.txt.bak, which is a copy of the source file, the other is test.txt, where \ r \ n is replaced by \ n.

Note If you look closely at the program above ($ extension = '.bak'; ...), you will see that if you call it like this: perl -ibak_ * ..., the backup file will be called “bak_test.txt”, that is, if there is an asterisk in the value of the parameter i, then this value is considered not as an extension, but as a template, where the asterisk denotes the name of the file.

-M flag

The -M flag allows you to connect modules

For example, I want to know where the CGI module is:

for Windows:
 perl -MCGI -le "print $INC{'CGI.pm'}" 

for Linux:
 perl -MCGI -le "print \$INC{'CGI.pm'}" 

Recently, I needed to make chmod a + x to all files with the extension ".cgi",
but on the server the -R flag for chmod for some reason did not work, so what I did was something like this:
 perl -MFile::Find -e 'finddepth(sub {print $File::Find::name . "\n"}, "."})' | grep -P '\.cgi$' | perl -nle '`chmod a+x $_`' 

With this code, “perl -MFile :: Find -e 'finddepth (sub {print $ File :: Find :: name.„ \ N “},”. "})'" I called the finddepth function of the File :: Find module, which recursively went around the current directory and output the full file paths.

Then, with the grep, I took only those files that end in '.cgi' (-P means that barley regular expressions are used), and with the following program “perl -nle '` chmod a + x $ _`' ”I made execution rights found files

Although I could write this code like this:
 perl -MFile::Find -e 'finddepth(sub {$n = $File::Find::name;`chmod a+x $n` if $n =~ /\.cgi$/}, ".")' 

Note that you need to use the -l flag so that the $ _ name gets the file name without "\ n"

And what if you need to connect some variables or subroutines from the plug-in package to the main package?
Then you need to write:
 perl -MModule=foo,bar -e '...'; 

or
 perl '-Mmodule qw(foo bar)' -e '...'; 

BEGIN and END


BEGIN and END can be used for actions that should occur at the beginning and at the end, in the same way as awk.
 perl -e 'BEGIN{< >};<>;END{ }'; 

For example, we will display lines consisting of 40 "=" characters at the beginning and end of the report:
 dir /b | perl -pe "sub line {print '=' x 40 . \"\n\"};BEGIN{line();};END{line()}" 

Debag


To debug single-line programs you need to connect the module B :: Deparse,

If you run:
 perl -MO=Deparse -ne "print 1" 

Then get the output:
 LINE: while (defined($_ = <ARGV>)) { print 1; } -e syntax OK 

module B :: Deparse should be connected like this: "-MO = Deparse", and not like this: "-MB :: Deparse". Apparently, this is done in order to clearly define that we want to use this module to display the source code of the program, and not just to use any of its methods in the program.

This is how the B :: Deparse module will be used as a normal module; no code will be output:
 perl -MB::Deparse -e "print 1" 

In the examples above, I used MO = Deparse to display the program code.

Examples of single-line programs


Output of the number of lines in the file (similar to Unix wc -l)
 perl -ne '}{ print $.' abc.txt 

Equivalent program:
 LINE: while (defined($_ = <ARGV>)) { (); } { print $.; } 

The tricky trick "} {" is used here. We ourselves have closed the cycle.

Binary number output
 perl -e "printf '%b', shift" 200 

Replace \ r \ n with \ n in the file
 perl -i.bak -pe 's/\r\n/\n/' file.txt 

Note For some reason, this code does not work in Windows: it stubbornly adds \ r \ n, I did binmode ARGV,
binmode $ ARGV, binmode * ARG {FILEHANDLE}, but nothing helped, I will continue to fight. I would be grateful if you write how to replace \ r \ n with \ n in Windows.

Conversion of IP addresses from the form of "digit-point" in the number:
 perl -e "print unpack('N', pack('C4', split /\./, shift))" 127.0.0.1 

Deleting .svn folders in the current folder and its subfolders (recursively)
 perl -MFile::Find -MCwd -e '$path = getcwd;finddepth(sub {print $File::Find::name."\n"}, "$path")' | grep '\.svn$' | perl -ne 'system("rm -rf $_")'; 

same for Windows:
 perl -MFile::Find -e "finddepth(sub{ print $File::Find::name . \"\n\"; }, '.')" | perl -ne "print if /.svn$/" | perl -pe "s|/|\\|g" | perl -ne "system(\"rd /s /q $_\");" 

Output IP address in hexadecimal
 perl -e "printf '%02x' x 4, split /\./, shift" 127.0.0.1 

Adding the line "#! / Usr / bin / perl" to the beginning of the file
 perl -i.bak -pe "print \"#!/usr/bin/perl\n\" if $. == 1" abc.pl 

For Linux / Unix:
 perl -i.bak -pe 'print "#!/usr/bin/perl\n" if $. == 1' abc.pl 

Why is this necessary?


As promised I will write, why all this is necessary. You can say that there is find, awk, grep, sed, why is single-line Perl?

Well, firstly, in Windows, by default, there is no grep and awk. Yes, of course, it is faster to use grep to select lines, but what if you need to do a little more, for example, rename a file? You will say there is, after all, find, yes there is. So what do I say in defense of the single-line pearl?

Here's what:

First, if you program in Perl, you remember Perl very well and you can immediately start writing a one-line program without looking at man. (At first, however, it may be a little unusual, but when you get involved it will be easy)

Secondly, it is often convenient to use Perl. For example, when I want to have an awk equivalent (see the -a flag) with Perl power (for example, I want to use the pack, unpack, or regular Perl expression functions in a one-line program)

Thirdly,
Perl is a powerful language. A single-line perl program is a regular Perl program, only on the command line. So one-line programs for pearl can be used for a variety of tasks! (But I think, it’s probably better not to write long one-line programs, it’s better to make a regular pearl-barley script).

Conclusion


Do not think only that I urge to abandon grep, find, sed or awk. I do not call! I myself continue to use grep, find. I just wanted to talk about another useful tool as a “single-line pearl”, which is convenient for Perl programmers, because: 1) you don't need to read man (you already remember everything), 2) the power of Perl is used

Thanks for attention. Those who are interested send here:

perldoc.perl.org/perlrun.html - perldoc, description of all flags.
sial.org/howto/perl/one-liner - various examples

I advise you to google pearl barley single-line programs with the word: one-liners

Update:
To replace \ r \ n with \ n in Windows, you just need to write:
 perl -i.bak -ple "s/\r|\n//g;binmode ARGVOUT" file.txt 

Respect AntonShcherbinin

Update:
Added to the code from AntonShcherbinin, " s / \ r | \ n // g; ", and then in Linux just binmode does not roll, now this code is universal: it works in Windows and Linux.

Update (August 26, 2012):
Rewrote code examples under <source lang = "Perl"> </ source> tags

Source: https://habr.com/ru/post/70610/


All Articles