nysus has asked for the wisdom of the Perl Monks concerning the following question:

I've got a suite of modules located in ~/perl5/my_modules which I have in my @INC array. I want to give my modules access to scripts that probably will only be used by my Perl modules. For example, I have an applescript that exports my contacts to a vcf file. So I want to put this script in a central directory where it can be found: ~/perl5/my_modules/bin and run with a system command. I have a MyFindBin module which I use to generate the location of this bin directory using a $MyBin variable that it exports in case the path changes.

What do others think of this approach? Is there a better, more perlish way?

$PM = "Perl Monk's";
$MC = "Most Clueless Friar Abbot Bishop Pontiff Deacon Curate Priest Vicar Parson";
$nysus = $PM . ' ' . $MC;
Click here if you love Perl Monks

Replies are listed 'Best First'.
Re: Looking for suggestions for accessing/running scripts needed by a local Perl library
by Bod (Parson) on Jan 23, 2024 at 23:28 UTC
    What do others think of this approach?

    Two things immediately strike me...

    1. Normally modules are used by scripts. Not the other way around. So my initial design choice would be to encapsulate the functionality of your scripts into their own modules. If more than one script needs VCF export functionality, that should be in a module. which can then be used by other modules and/or by scripts.
    2. Using system to run Perl scripts from within Perl is bad practice - I don't understand the nuances of what problems it causes (hopefully others will educate us both), but I do know it's not something you should do. With some thought about design, it should never be necessary either.

      WRT #1, my modules sometimes need to execute scripts like the export functionality I mentioned. There is no way I could find to dump out Contacts without a GUI other than using an applescript.

      WRT #2, I think it's mostly a security issue and it's easy to inject bad stuff into poor written command run by system. But since my modules are just for my own use on my own, local machine, I'm not sweating any security issues. I've got bigger problem if someone has hacked my machine and can pass nasty things to my perl scripts.

      $PM = "Perl Monk's";
      $MC = "Most Clueless Friar Abbot Bishop Pontiff Deacon Curate Priest Vicar Parson";
      $nysus = $PM . ' ' . $MC;
      Click here if you love Perl Monks

        If I wanted to do it quickly because the details didn't matter much, I would just take the non-perl scripts and give them a distinctive name (unlikely to interfere with regular commandline use) and put them in ~/bin

        If I wanted a clean solution that was CPAN-looking (even if not going there yet) I would build a module with Dist::Zilla and put the non-perl scripts in ./share and then access them with File::ShareDir.

        Any external script that *was* perl, I would refactor into a module and put it in my module path. Well.. unless it was massive and complex and written by someone else, then maybe I would just shell out to it. But I would at least *intend* to refactor it into a module some day.

Re: Looking for suggestions for accessing/running scripts needed by a local Perl library
by swl (Prior) on Jan 23, 2024 at 23:37 UTC

    I'll second bod's thoughts in 11157199.

    Some additional thoughts:

    If you want to use your script as both a script and a module then have a look at the modulino idea.

    Your MyFindBin code might be simplified by using FindBin, possibly in tandem with lib. There is also rlib for relative file structures. Maybe your code uses these approaches already.

      Thanks, yeah, I was looking at the FindBin module. But I'm just going to dump all the scripts in the same directory and there's a change, albeit small, this location can change. So I wanted that to be dynamic, so I threw this code into my own module:

      our $MyBin; BEGIN { my $file = __FILE__; # get the path to the directory of this file using File::Basename my $package = __PACKAGE__; $package =~ s/::/\//g; # chop off the package name from the end of the file path $file =~ s/$package\.pm//; $MyBin = $file . 'bin'; }
      But maybe I'm not understanding FindBin

      $PM = "Perl Monk's";
      $MC = "Most Clueless Friar Abbot Bishop Pontiff Deacon Curate Priest Vicar Parson";
      $nysus = $PM . ' ' . $MC;
      Click here if you love Perl Monks

Re: Looking for suggestions for accessing/running scripts needed by a local Perl library
by bliako (Abbot) on Jan 24, 2024 at 09:34 UTC
    ... Is there a better, more perlish way?

    There is surely a more unixish way: create a user-specific usr dir with all its usual children (e.g. ~/usr/bin) at ~/usr

    I install all my private/personal scripts (bash, applescript in your case) into ~/usr/bin

    For my personal Perl modules, I set INSTALL_BASE=$ENV{HOME}.'/usr' in their Makefile.PL (ExtUtils::MakeMaker) or (for not forgetting this crucial parameter in the Makefile.PL when uploading to CPAN -- lots of times!) I use the command-line mantra: PERL_MM_OPT=INSTALL_BASE=~/usr perl Makefile.PL && make all && make test && make install In this way, the module (just this one time) is installed under ~/usr

    Some of my modules provide scripts for command-line functionality. In this case I set EXE_FILES of ExtUtils::MakeMaker (an array of script filepathnames) in Makefile.PL and all are installed under ~/usr as per the INSTALL_BASE setting.

    Finally, my bin-path, ld-library-path, INC, are all appended with the usual children of ~/usr (which mirrors the structure of /usr btw).

    Bottom line: when in doubt I always create a Module.

    And if the initial and final aim was just a command line Perl script, then so be it. I temporarily suspend my usual lazyness and take the extra step to peace of mind (future-proof is a word I encountered lately) with perl Makefile.PL && make all && make test && make install : a consistent and portable way for installing even the most trivial Perl script.

    BTW, all my Perl modules' source dirs are living under (say) ~/PERLSRC . Whenever I want to migrate, circummigrate or emigrate, I just copy that dir to a stick and take it with me (I know I know, you gitters!). It's as good in my machine as in any other machine. A simple (Edit: edited the line below to use -execdir, i.e. execute inside the dirname of the matched file - it will fail if '.' is in the PATH, in which case you need to remove '.' from the PATH):

    PERL_MM_OPT=INSTALL_BASE=~/usr find PERLSRC -type f -name 'Makefile.PL +' -exec echo \{\} \; -execdir perl \{\} \; -execdir make all \; -exec +dir make install \;

    installs them as before for me. And if you want them installed in a certain order, then create a Makefile or a bash script.

    Edit: just to clarify: the main idea is to place the scripts in standard locations which are in your INC/PATH and can be found with, say, File::Which. Also, when writing the above I had also in mind your meditation Tip: Create a "working" perl lib for your personal use

    bw, bliako

Re: Looking for suggestions for accessing/running scripts needed by a local Perl library
by Arunbear (Prior) on Jan 24, 2024 at 11:51 UTC
    Your approach seems reasonable given your particular use case.

    Another option is that your modules could read a config file, or an environment variable to learn the location of your scripts.

Re: Looking for suggestions for accessing/running scripts needed by a local Perl library
by Anonymous Monk on Jan 24, 2024 at 17:32 UTC

      from DESCRIPTION of File::ShareDir:

      Perl provides [i.e. "to have access to a large amount of read-only dat +a that is stored on the file-system at run-time" ] a little-known met +hod for doing this, but almost nobody is aware that it exists.

      do you know what this little-known method is?

        I first came across File::ShareDir about five or so years ago. Like you, I found the esoteric "little-known method" description unhelpful and confusing.

        I've used it in conjunction with File::ShareDir::Install. I've only used the simplest case (as described in the SYNOPSIS): I found it straightforward, easy to use, and haven't encountered any problems with it.

        — Ken

Re: Looking for suggestions for accessing/running scripts needed by a local Perl library
by InfiniteSilence (Curate) on Jan 23, 2024 at 23:41 UTC

    My guess is that you want to keep this stuff in Perl scripts because you only occasionally use this functionality. I would convert my scripts both to actual modules and in some cases methods that load via AutoLoader.

    Main Script
    #!/usr/bin/perl -w use strict; use lib qq~./mydir~; require qq~./code.pm~; #use Foo; print Foo::foo(); 1;
    Module
    package Foo; use AutoLoader 'AUTOLOAD'; # import the default AUTOLOAD subroutine 1;
    Autoloaded Stuff

    Note that your .al files have to be in a particular directory. in this case it is here: ./mydir/auto/Foo/foo.al

    package Foo; sub foo { return 'tastes so good...'; } 1;
    Try it Out
    perl code.pl tastes so good...

    Celebrate Intellectual Diversity

      So this is like an AUTOLOAD for packages instead of subroutines?

      $PM = "Perl Monk's";
      $MC = "Most Clueless Friar Abbot Bishop Pontiff Deacon Curate Priest Vicar Parson";
      $nysus = $PM . ' ' . $MC;
      Click here if you love Perl Monks