Single line Perl programs

    Introduction


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

    The goal 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 this is necessary.

    Well, first things first.

    Flags


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

    Note. When we run single-line pearl bar code from under Windows, we must enclose the code in double quotes:
    perl -e "print 0xFA23B"
    , in the case of Linux / Unix, the code can be both in double quotes and in single quotes, but in Unix / Linux, the case of double quotes has to be escaped with the signs "$":
    perl -e "\$i = 0;print \$i"

    Note. After the -e flag, the code should immediately follow .

    The -l
    flag This flag does initializes the variables $ / and $ \ with the value "\ n";
    The variable $ / defines the input field separator.
    The $ \ variable specifies what will be printed after the print command.

    Program:
    perl -le "print 1"

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

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

    Flag -n
    From here the fun begins.

    The following code:
    perl -ne 'print 1'

    equivalent to this:
    LINE: while (defined($_ = )) {
       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($_ = )) {
        chomp $_;
        rename $_, "$_.bak" if /^\d+/;
    }
    

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

    The -a
    flag The -a flag allows you to use Perl as awk.

    The following code:
    perl -nae "print 1"

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

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

    Suppose you want to output the user names 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 find out the names of files in a folder that I last changed in September 2009:
    dir /TW | perl -nale "print $F[$#F] if $F[0] =~ /\.09\.2009/"

    The -p
    flag This flag also does that -n only adds another continue block with “print $ _”.

    The following code:
    perl -pe "print 1"

    equivalent to:
    LINE: while (defined($_ = )) {
        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/"

    The -i
    flag The i flag allows you to modify files.

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

    equivalent to this:
    BEGIN { $^I = ".bak"; }
    LINE: while (defined($_ = )) {
        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);
    

    I’ll briefly explain what happens when the lines perl -i.bak -pe “code” <file name> 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, even though the file was renamed, do we still have access to its lines?)

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

    As a result of this code, two files will be obtained: one is test.txt.bak, which is a copy of the source, 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 like this: perl -ibak_ * ..., then the backup file will be called “bak_test.txt”, that is, if 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 file name.

    Flag -M

    flag -M allows you to connect modules

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

    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 for some reason the -R flag for chmod did not work on the server, so this is what I did, 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 bypassed the current directory and displayed the full file paths.

    Then I took only those files that end with '.cgi' (-P means that pearl barley regular expressions are used), and with the next program “perl -nle '' chmod a + x $ _``” I made execute permissions 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 $ _ gets the file name without "\ n"

    But what if you need to connect some variables or routines 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


    You can use BEGIN and END for actions that must occur at the beginning and at the end, similar to awk.
    perl -e 'BEGIN{<начальные действия>};<действие>;END{конечные действия}';

    For example, output lines consisting of 40 "=" signs 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 B :: Deparse module.

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

    You will get the output:
    LINE: while (defined($_ = )) {
        print 1;
    }
    -e syntax OK
    

    the B :: Deparse module needs to be connected like this: "-MO = Deparse", and not like this: "-MB :: Deparse". Apparently this was 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 regular module, there will be no code output:
    perl -MB::Deparse  -e "print 1"

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

    Single line example programs


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

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

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

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

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

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

    Converting an IP address from a digit-to-dot form to a number:
    perl -e "print unpack('N', pack('C4', split /\./, shift))" 127.0.0.1

    Removing .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 thing 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 $_\");"

    Hexadecimal IP Address Output
    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 single line Perl?

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

    And here's what:

    First, if you program in Perl, then 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 convenient to use Perl. For example, when I want to have an analogue of awk (see the -a flag) with the power of Perl (for example, I want to use the pack, unpack, or regular expression functions of Perl in a single-line program)

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

    Conclusion


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

    Thank you for your attention. Those who are interested refer here:

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

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

    obn:
    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; ", otherwise it just doesn’t roll binmode in Linux, now this code is universal: it works in both Windows and Linux.

    Update (08.26.2012): I
    rewrote the code examples under the tags

    Also popular now: