Running a Perl script from crontab when you use Perlbrew
Asked Answered
I

2

7

I have tried the following and find it to work. This is done with a non-privileged user. First find out where your perl command is:

# which perl

Then check the value of PERL5LIB:

# echo $PERL5LIB

Then, at the crontab file of the user, do something like:

MAILTO=<my email address for the jobs output>
HOME=/home/myhome
PERL5LIB=/home/myhome/perl5/lib/perl5

0 2 * * * $HOME/<rest of path to perl>/perl $HOME/<path to my perl script> arg1 ...

This will run a job at 2am and seems to find all Perl libs correctly. My question is: is this complete and portable? Is there a better way?

I have seen a number of bash and perl scripts out there that are supposed to prepare the environment for the execution of a Perl script, but this seems to suffice. Any advice will be welcome!

EDIT: From the comments to the question, it seems that I am using a "bad" mixture of Perlbrew and local::lib. The way to make sure libraries get installed inside a particular Perlbrew version is answered here: How do I install CPAN modules while using perlbrew?. Both cpan and cpanm will install under PERL5LIB when you are using local::lib unless you explicitly tell them to do otherwise. Also cpanm seems to be better suited to working along with Perlbrew.

Invade answered 23/11, 2017 at 14:31 Comment(20)
Take a look and read about the Ubic. It is sure an overkill for your use-case, but you could get some ideas...Erena
You should be able to run something like `bash 'perlbrew use perl-5.28.0; perl $HOME/<path to my perl script>' from cron. That will pick a specific Perl for that sub-shell and set everything automatically.Spleenful
@Spleenful Except that perlbrew use seems to exec into a new shell, so you'd need to pipe commands into it: perlbrew use perl-5.26 <<<'perl ./some-script.pl' (Bash syntax). perlbrew exec --with perl-5.26 'perl ./some-script.pl' would be equivalent, but prints an annoying header before the output.Aara
@jm666, thank you for the suggestion. Ubic seems like overkill as you say, but it does look interesting for services management in general.Invade
It doesn't make much sense to use local::lib with perlbrew, so you shouldn't be setting PERL5LIB.Gilbertine
@ikegami, how then? It is what makes my simplistic way of running Perl scripts work. If I don´t set PERL5LIB I only get some of the @INC directories in the running Perl script and then use will not consider certain libraries ending up in a compile-time error.Invade
Re "I only get some of the @INC directories in the running Perl script", Right, and that's where the modules you should be installed. It makes no sense to install them elsewhere.Gilbertine
@ikegami, I am using cpanm <module> to install modules and they end up in the PERL5LIB directory. cpan seems to do the same. Are you suggesting that they should be installed for each specific Perl version? I am using Perlbrew to have a local Perl (and libraries) for a user, not to use more than a single Perl version, so I was not paying attention to the place libraries were going.Invade
Re "and they end up in the PERL5LIB directory.", That's because you are using install::lib and you shouldn't. It's likely to cause problems, and there's no reason to use it.Gilbertine
@ikegami, ok, I think that now I see what you mean. local::lib is meant to have libraries local to a user but with the system-wide Perl. Perlbrew is meant to have a whole Perl + libraries environment local to a user. Am I right? But do you see a problem if I stick to a single Perlbrew interpreter? It would take some effort to go back...Invade
@Aara --- to surprise that annoying message, you can tell perlbrew to be --quiet. I use it in some off my ~/.bash_prfofile aliases that require some perl modules. But I do not want to clutter my installed Perls like alias minidoc='perlbrew exec --quiet --with perl-5.26.1@minicpan mcpandoc -MPod::Text::Color::Delight'Nettienetting
@amon, Re "Except that perlbrew use seems to exec into a new shell,", Not if you have perlbrew properly setup. There's a file that needs to be sourced at shell startup. You're describing the backup behaviour when that isn't done.Gilbertine
Re "But do you see a problem if I stick to a single Perlbrew interpreter?", I don't see how you can do that. Lots of things use the system perl, and you'll need to upgrade eventually.Gilbertine
@ikegami, I don´t see what you mean. I have a non-privileged user that uses local::lib so its PERL5LIB variable points to a directory of this user and then Perlbrew to have a different Perl version than the system´s one. The libraries get installed under the PERL5LIB directory which is local to the user. The crontab file I refer to is also the user´s.Invade
@ikegami, sorry, I have read my question and see I don´t mention the user anywhere. I have edited it to make this clear.Invade
Re "I don´t see what you mean.", You said you could guarantee that the only perl a user would only use was a specific one installed using perlbrew. I said I don't believe you can guarantee that. Lots of stuff use the system perl.Gilbertine
Re "I have a non-privileged user that uses local::lib [with a perl installed in their home dir]", I know that. They shouldn't be using local::lib. local::lib is a hack whose need was eliminated by the installation of a perl to which they have full access.Gilbertine
@Gilbertine Re new shell: oh. oooh! That explains a lot. Thank you very very much for your pointer.Aara
Re "Both cpan and cpanm will install under PERL5LIB", That's not true. They don't use PERL5LIB.Gilbertine
It should be both cpan and cpanm install under PERL5LIB by default when you have local::lib. Fixed in the question. Thanks.Invade
G
2

The shebang (#!) line of the script should point to the (perlbrew-installed) perl it is meant to run under. (This should be done as part of installing the script.) That's all you need.

0 2 * * * /path/to/script arg1 ...
Gilbertine answered 23/11, 2017 at 20:59 Comment(7)
++ this is exactly why Gugod introduced the alias feature after removing the "current" symlink. See: github.com/gugod/App-perlbrew/issues/142Sovran
I assume that you could use a regular shebang and invoke the right Perl interpreter from crontab as 0 2 * * * /path/to/perl /path/to/script arg1 .... This would let you move the script around without hardcoding the Perl interpreter that it needs. Am I right?Invade
Yes, you could hardcode it in the cron job and everywhere else you use it instead, but that's bad!!! It's supposed to be hardcoded in the script when you install it. That way, the script is only used with the perl under which it was tested and installed. The standard Perl installers even rewrite #!/usr/bin/perl into the proper path automatically for you. It saves you from hardcoding it in a million places (e.g. the cron files), or from using an installation that's not wasn't setup for it. I stand by my answer.Gilbertine
@ikegami, thanks a lot for the explanation! It makes a lot of sense. My doubts come from the fact that I tend to code in Perl in a version-independent way. Probably because I tend to use the system´s Perl interpreter in cPanel managed machines, which change it over time..Invade
I'm not sure why you think that's different. Don't your scripts that use the system perl have #!/usr/bin/perl as their first line?Gilbertine
Well, you may not like it :-), but it is possible to use /usr/bin/env perl to make sure the system finds a Perl interpreter for you in your PATH. You cannot guarantee which version but I guess use <version> was created for that.Invade
It's not a question of liking or not. That's a horrible idea. It causes the script to break when you use perlbrew since it changes your path.Gilbertine
S
2

If you already have multiple perl installations managed with perlbrew the easiest approach is to just use perlbrew exec to run your script. The -q and --with options allow you to silence superfluous output and select the specific version of perl to run the script/job. Try something like:

  • perlbrew exec perl -E 'say "Hello from $]\n"' (this will show errors from older versions (< 5.10) of perl that don't have the -E switch enabled by default).
  • perlbrew exec -q --with 5.26.1 perl -E 'say "Hello from $]\n"' (this will run the command and suppress informational output).
  • perlbrew exec -q --with 5.26.1 perl ~/script_from_heaven.pl (runs the script with the perl version requested).
  • perlbrew exec -q --with 5.26.1 ~/script_from_heaven.pl (runs the script with the perl version requested or hard-coded in the script's shebang line).

I tend to explicitly set PERL5LIB and use local::lib only when I need them or for certain users or environments where I exclusively install all CPAN modules in $HOME/perl5/lib/perl5 (a full application deployment, say). Otherwise I find running perl from perlbrew pretty convenient.


A couple of things I've found helpful: setting an alias for perlbrew environments that you want to keep stable for a particular use can be a useful way to manage multiple perls:

 ~/$ perlbrew alias create perl-5.24.0 stable-cronperl
 ~/$ perlbrew list
 perl-5.8.9
 perl-5.10.1
 perl-5.24.0
 cperl-cperl-5.26.1
 stable-cronperl (5.24.0)
 perl-5.26.1

NB: however the alias is only useful/useable as a stable #! shebang anchor for use at the top of your scripts if you want to make them executable:

#!/home/cronic/perl5/perlbrew/perls/stable-cronperl/bin/perl

You can't refer to an alias using --with for example:

perlbrew exec --with stable-cronperl ~/smart_comments.pl

Reporting this as either a documentation issue or a bug is on my to do list.

Sovran answered 23/11, 2017 at 21:7 Comment(2)
There is also Tokuhirom's plenv ;-) Miyagawa has been adding the "missing" perlbrew commands to plenv: github.com/miyagawa/plenv-contribSovran
I find your answer more complete, but ikegami´s more correct. So I will mark his as correct but give you my upvote.Invade

© 2022 - 2024 — McMap. All rights reserved.