http://qs1969.pair.com?node_id=1183251


in reply to Re^3: Using guards for script execution?
in thread Using guards for script execution?

Also, useing a Perl file does not execute it,

This is SO wrong that I can't leave it uncommented, even if you intended to simplify.

AnomalousMonk++ already explained that used modules are evaluated.

To explain that a little bit more: use is an extended require happening at compile time, as if wrapped in a BEGIN block. (BTW: [doc://BEGIN] does not link properly.) require is an extended do FILENAME that prevents loading the same file more than once and checks the result of loading the file. This is why modules need to return a true value. And finally, do FILENAME behaves like an extended eval STRING, where the string is loaded from a file searched in @INC. All of this is documented in perlfunc.

But there is more:

Loading any module does not only parse the file and execute code outside the subroutine definitions. Modules can contain five different code blocks (BEGIN, UNITCHECK, CHECK, INIT, and END) that are executed outside the normal flow. Most notably, BEGIN blocks run while code is still being compiled; UNITCHECK and CHECK run after compilation, INIT runs before runtime, END after runtime. This is documented in perlmod.

Loading a module using the use function has yet another side effect, as documented in use:

[use Module LIST] is exactly equivalent to

BEGIN { require Module; Module->import( LIST ); }

So, in addition to the "magic" code blocks, a method in the module is invoked. (If the module has no import method, the one implicitly inherited from UNIVERSAL is invoked.) Again quoting use:

If you do not want to call the package's import method (for instance, to stop your namespace from being altered), explicitly supply the empty list:

use Module ();

That is exactly equivalent to

BEGIN { require Module }

So, loading a module using use, or even using require, actually runs a lot of code in that module:

demo.pl

#!/usr/bin/perl use strict; use warnings; BEGIN { print ">>> About to execute 'use LoadMe;' <<<\n"; } use LoadMe; BEGIN { print ">>> LoadMe was loaded <<<\n"; } print "***** See? No code in LoadMe is executed! *****\n";

LoadMe.pm

package LoadMe; use strict; use warnings; INIT { print "LoadMe: INIT is evaluated\n"; system "echo Would I run rm -rf / ?"; } CHECK { print "LoadMe: CHECK is evaluated\n"; system "echo Check check, is this thing on?"; } UNITCHECK { print "LoadMe: UNITCHECK is evaluated\n"; system "echo Luckily, format C: does not work on linux"; } BEGIN { print "LoadMe: BEGIN is evaluated\n"; system "echo what could go wrong?"; } END { print "LoadMe: END is evaluated\n"; system "echo Good bye cruel world"; } sub foo { print "LoadMe: foo\n"; } sub import { my $class=shift; print "LoadMe: import called as ${class}->import(",join(',',@_),") +\n"; system "echo Oh well, no smart comment here"; } print "LoadMe: Module initialisation\n"; system "echo Perl is fully working and can run arbitary code, includin +g external programs"; 1; # or any other non-false value

Demo:

/tmp/use-demo>perl demo.pl >>> About to execute 'use LoadMe;' <<< LoadMe: BEGIN is evaluated what could go wrong? LoadMe: UNITCHECK is evaluated Luckily, format C: does not work on linux LoadMe: Module initialisation Perl is fully working and can run arbitary code, including external pr +ograms LoadMe: import called as LoadMe->import() Oh well, no smart comment here >>> LoadMe was loaded <<< LoadMe: CHECK is evaluated Check check, is this thing on? LoadMe: INIT is evaluated Would I run rm -rf / ? ***** See? No code in LoadMe is executed! ***** LoadMe: END is evaluated Good bye cruel world /tmp/use-demo>

Another demo:

Perl has a -c switch, which has a nice, seemingly harmless first sentence in its documentation:

-c causes Perl to check the syntax of the program and then exit without executing it.

Except that this is completely wrong, as explained in the following sentence:

Actually, it will execute and BEGIN, UNITCHECK, or CHECK blocks and any use statements: these are considered as occurring outside the execution of your program. INIT and END blocks, however, will be skipped.

So what actually happens is that perl stops after the compile phase has completed, and instead of starting the run phase, it will print a success message unless the compile phase has failed.

This has consequences. Some code is even executed if you only intent to check the code:

/tmp/use-demo>perl -cw demo.pl >>> About to execute 'use LoadMe;' <<< LoadMe: BEGIN is evaluated what could go wrong? LoadMe: UNITCHECK is evaluated Luckily, format C: does not work on linux LoadMe: Module initialisation Perl is fully working and can run arbitary code, including external pr +ograms LoadMe: import called as LoadMe->import() Oh well, no smart comment here >>> LoadMe was loaded <<< LoadMe: CHECK is evaluated Check check, is this thing on? demo.pl syntax OK /tmp/use-demo>perl -cw LoadMe.pm LoadMe: BEGIN is evaluated what could go wrong? LoadMe: UNITCHECK is evaluated Luckily, format C: does not work on linux LoadMe: CHECK is evaluated Check check, is this thing on? LoadMe.pm syntax OK /tmp/use-demo>

Nothing of this is new (except for UNITCHECK, introduced in perl 5.10), but anyone should be aware of those features. Pretending that loading a module does not execute any code in the module does not help.

Alexander

--
Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)