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

I love the simplicity and power of one-liners like

perl -pie 's/foo/bar/g;'

Is there a way to invoke functionality like this from the middle of a longer perl script without forking another perl instance? I'm thinking along the lines of

sub delete_comments_from_file { my( $path ) = @_; eval "local @ARGV=qw{ -pie s/#.*//; $path };"; }

This would be quicker to write, clearer and less error prone than writing it all long-hand and I know the functionality is all there. I just don't know how to unlock it.

<°}}}>«<

Replies are listed 'Best First'.
Re: Avoid re-invocation for -pie processing
by 1nickt (Canon) on Jan 05, 2024 at 19:20 UTC

    Hi, use Path::Tiny and edit_lines().

    use strict; use warnings; use feature 'say'; use Path::Tiny; my $file = path('file.txt'); $file->spew("foo bar\nfoo bar"); $file->edit_lines(sub{s/bar/baz/}); say $file->slurp;
    Output:
    foo baz foo baz

    Hope this helps!


    The way forward always starts with a minimal test.

      Thanks, 1nickt. That will do exactly what I want.

      <°}}}>«<
Re: Avoid re-invocation for -pie processing
by tybalt89 (Monsignor) on Jan 05, 2024 at 19:23 UTC

    These days I would prefer something like:

    #!/usr/bin/perl use strict; # https://perlmonks.org/?node_id=11156715 use warnings; use Path::Tiny; sub delete_comments_from_file { path($_)->edit( sub { s/#.*//g } ) for @_; } my $file = '/tmp/foobar.tmp'; path($file)->spew( <<END ); one # a comment two no comment three # more comments; END print path($file)->slurp; print '-' x 50, "\n"; delete_comments_from_file( $file ); # NOTE print path($file)->slurp;

    Outputs:

    one # a comment two no comment three # more comments; -------------------------------------------------- one two no comment three

    There is also a edit_lines() version for 'one-line-at-a-time'.

Re: Avoid re-invocation for -pie processing
by Anonymous Monk on Jan 05, 2024 at 18:25 UTC
Re: Avoid re-invocation for -pie processing
by ikegami (Patriarch) on Jan 08, 2024 at 14:41 UTC
    local @ARGV = $path; local $^I = ""; while ( <> ) { s/foo/bar/g; print; }

    Assumes ARGV is currently at EOF.

      I sometimes use the following, which also resets *ARGV and $ARGV:

      local *ARGV = [$path];

      ... but most of the time, I forget about the other slots...

      Update: And I learned something. That stanza above does not reset $ARGV:

      perl -wlE 'sub say_argv { say @ARGV, $ARGV; }; $ARGV=9; say_argv; do { + local *ARGV = ["foo"]; say_argv; }; say_argv' 1 2 3 1239 foo9 1239

        If you're not in the middle of using <> (since the snippet I posted doesn't work if you're already in the middle of reading from ARGV), then it doesn't really matter if you clobber *ARGV{IO} and $ARGV.

Re: Avoid re-invocation for -pie processing
by InfiniteSilence (Curate) on Jan 06, 2024 at 23:54 UTC

    Assuming the Example is Just an Example

    Maybe I read between the lines a little too much but my guess is that your example is just that, an example, and you really are asking how to avoid recompiling code and repeatedly invoking Perl. The answer to that is likely a process (crude but effective):

    use strict; use Daemon::Easy sleep=>5, stopfile=>'stop', pidfile=>'pid', callback= +>'worker'; use File::Find qw~find~; run(); sub worker { die 'Improper setup -- must have an ./old folder!' unless (-d './old +'); die 'Improper setup -- must have a ./pass folder!' unless (-d './pas +s'); find(\&wanted, qw~/tmp/working/perlmonks/lib/11156715~); } sub wanted { if ( (-f $File::Find::name) && ($File::Find::name=~m/\.pl$/ ) && ($File::Find::name !~m/code\.pl$/) ) { print "open the file, replace comments, resave orig. to ol +d folder and updated one to pass folder --- $File::Find::name\n"; } }

    Errr....you can do this in like a bazillion other ways in Perl. Off the top of my head you could use Dancer or Mojolicious or just hack up an HTTP server, etc.

    Otherwise...

    Here is where I struggled with your question -- why, oh why, are you trying to optimize something as trivial as this? I mean, it should be a one liner. I normally wrap stuff like this in a bash script and leave it alone:

    #!/bin/bash perl -pi.bak -e 's{a}{b}g' $1

    Epilogue

    I have been messing around with Javascript so much that I started thinking about ways to make asynchronous calls after copying a file to my folder. Perhaps I would use this Future module...

    Celebrate Intellectual Diversity

      Hi,

      My example absolutely should be a one-liner - if invoked from the shell. What I wanted to do, however, was achieve the same thing from inside a lengthy perl script (that does lots of other things) without (a) having to code the whole thing with proper error-checking etc and without (b) forking another perl just to save typing.

      Ikegami's answer is closest to the spirit of my request but the answers that suggest using Path::Tiny are probably easier to read, which is a "good thing"™.

      Thanks for your interest.

      <°}}}>«<