0

I have the following perl one-liner to convert /path/to/file.txt to /path/to/

echo "/path/to/file.txt" | perl -pe 's{(.*)}{File::Basename->dirname($1)}ge' 

but I'm missing something in my invocation of File::Basename->dirname(), causing the following error:

Can't locate object method "dirname" via package "File::Basename" (perhaps you forgot to load "File::Basename"?) at -e line 1, <> line 1.

What am I missing?

(I know I can just use dirname from bash but I'm trying to do something more complicated with perl than what this stripped down example shows).

Sridhar Sarnobat
  • 25,183
  • 12
  • 93
  • 106
  • answer for my future copy-paste needs: `echo "/path/to/file.txt" | perl -MFile::Basename -pe 's{([^\n]+)}{dirname($1)}ge'` – Sridhar Sarnobat Nov 20 '16 at 01:40
  • 1
    Note that this way you are importing _all_ functions from this module. To import (only) a specific one, use `-MModule=func` – zdim Nov 20 '16 at 01:44
  • Good to know. As it turns out I need to use `dirname` AND `basename` so that worked out conveniently :D – Sridhar Sarnobat Nov 20 '16 at 02:01

3 Answers3

1

Load a module with -MModName=func

perl -MFile::Basename=dirname -pe 's{(.*)}{dirname($1)}ge' 

The File::Basename module exports all its functions by default so you don't need =dirname above. But this varies between modules, and mostly you do need to import symbols. For more on how to do that in a one-liner, find the -M switch in Command Switches in perlrun.

zdim
  • 64,580
  • 5
  • 52
  • 81
  • Bingo, I forgot the module isn't loaded without `-M`. – Sridhar Sarnobat Nov 20 '16 at 01:37
  • Just to expand on this (I'm trying to create some HTML), you can mix text with evaluation with `@{dirname($1)}` without the `/e` switch. The only cost is that I can't use mu usual `s{}{}g` structure and have to revert to the (inferior but) more common `s//g` structure – Sridhar Sarnobat Nov 20 '16 at 01:57
  • @Sridhar-Sarnobat It sounds like you may want to switch to a script ... ? It's all going to suddenly become far easier :) – zdim Nov 20 '16 at 02:06
  • I know where you're coming from. Currently I'm waging a war against "monster monoliths" as I call them and (over?) utilizing pipes for composability to see how far Doug McIlroy's "Unix Philosophy" can take me when doing rapid prototyping. But yes it's approaching its limits. – Sridhar Sarnobat Nov 20 '16 at 02:08
1

Error #1:

Like the message suggests (perhaps you forgot to load "File::Basename"?), you need to load File::Basename.

perl -pe'use File::Basename; ...' 

or

perl -MFile::Basename -pe'...' 

Error #2:

dirname is not a method, so File::Basename->dirname is incorrect. It needs to be called as File::Basename::dirname.

perl -MFile::Basename -pe's{(.*)}{File::Basename::dirname($1)}ge' 

You could also import dirname.

perl -MFile::Basename=dirname -pe's{(.*)}{dirname($1)}ge' 

Fortunately, File::Basename exports dirname by default, so you can simply use

perl -MFile::Basename -pe's{(.*)}{dirname($1)}ge' 
ikegami
  • 367,544
  • 15
  • 269
  • 518
0

The problem was that you had <button> in the replacement string, but you've removed that now so it should work

The replacement string must be a valid Perl expression if you're using the /e modifier, and

<button>File::Basename->dirname($1)

isn't valid Perl


The correct command would be:

echo "/path/to/file.txt" | perl -pe 'use File::Basename 'dirname'; s{([^\n]+)}{dirname($1)}ge' 
Sridhar Sarnobat
  • 25,183
  • 12
  • 93
  • 106
Borodin
  • 126,100
  • 9
  • 70
  • 144
  • After removing `button`, I get `Can't locate object method "dirname" via package "File::Basename" (perhaps you forgot to load "File::Basename"?) at -e line 1, <> line 1.` . I'll update the question. – Sridhar Sarnobat Nov 20 '16 at 01:33
  • 1
    Take a look at the [documentation for `File::Basename`](https://metacpan.org/pod/File::Basename). The module *exports* `dirname`; it's not an object oriented module. So you need `use File::Basename 'dirname'` and then you can call it with just `dirname($1)`. – Borodin Nov 20 '16 at 01:37
  • ...which, in one-liner subsitution mode, is only possible with `perl -M` I guess. Thanks for the reply. – Sridhar Sarnobat Nov 20 '16 at 01:39
  • 1
    No. You can put `use File::Basename 'dirname';` inside the quotes if you like. It's exactly the same as adding `-MFile::Basename=dirname` as an option – Borodin Nov 20 '16 at 01:40
  • Oh, got it. I'll update your answer with the correction for my exact command. Thanks. – Sridhar Sarnobat Nov 20 '16 at 01:42
  • Hmmmm, I do want ` – Sridhar Sarnobat Nov 20 '16 at 01:46
  • In answer to my last question, I would use `' – Sridhar Sarnobat Nov 20 '16 at 01:52
  • ...so the full statement would be `echo "/path/to/file.txt" | perl -MFile::Basename=dirname -pe 's{([^\n]+)}{""}ges' ` – Sridhar Sarnobat Nov 20 '16 at 01:59