Hacker News new | past | comments | ask | show | jobs | submit login

I'm learning perl right now and I'm bewildered at how could I overlook it for such a long time.

With all its quirks and strange design decisions (especially compared to elegant languages like ML or Scheme), it has it's own killer features I missed from other scripting languages so much, such as the ability to embed a shell command in backquotes and get its output easily.

Also the notion of context is pretty good, allowing to write a far shorter code, even sigils contribute to that naturally (in languages like bash they feel like a useless burden).




Context is something you have to learn to be careful with but used judiciously is often quite neat.

Sigils are great once you realise they're conjugation, i.e. $ -> the, @ -> these, % -> the pairs of, hence

  $foo # the value of foo
  $foo{bar} # the bar value of the hash %foo
  @foo{qw(bar baz)} # the bar and baz values of the hash %foo
  %foo(qw(bar baz)} # the bar and baz pairs of the hash %foo
    # i.e. the last one returns (bar => $bar_value, baz => $baz_value)
(and similarly @foo[1,2,3] for 'these values of the array @foo')

I tend to prefer using p3rl.org/IPC::System::Simple and/or p3rl.org/Capture::Tiny and/or p3rl.org/IPC::Run3 over backticks, and p3rl.org/Path::Tiny over File::Spec (or p3rl.org/Mojo::Path if I'm in a Mojo(licious) app).

Note that for scripts where you don't want to deal with dependencies there's p3rl.org/App::FatPacker which will bolt pure perl deps (which all of the above are) onto the front of your script so you can still deploy a single file, p3rl.org/App::plx for managing a self-contained environment and p3rl.org/Object::Remote for when you don't want to need a script on the far side at all, just the ability to ssh to it and run -some- sort of perl binary.

(I've made quite a lot of effort over the years to minimise the number of times I hear the words "but I can't use CPAN" and them actually be true ;)

Plus for bigger stuff p3rl.org/Mojo::IOLoop and/or p3rl.org/IO::Async (the two event systems co-operate fine in a single process) with p3rl.org/Future::AsyncAwait turns out to be really handy.

(I wrote p3rl.org/Parallel::Map as a wrapper around p3rl.org/IO::Async::Function plus the fmap_ functions in p3rl.org/Future::Utils so I can drop fork based limited parallelism into the middle of a script without having to think about the event loop code underlying the implementation)


Where were you when I was learning Perl? That was the best description of context I've ever seen.

Context was one of those subjects where it's a little tricky but the way it was explained to me made it a complete nightmare to understand.


I picked up perl about 18 months ago and I never really found context hard to understand at all. Not trying to boast- just not really sure what people find hard about it.


The first thing is that it's a little like pointers where it's not too hard but there are a lot of explanations of it that create mystification rather than knowledge.

Stuff like this: https://www.perlmonks.org/?node_id=738558

> There is a way for a user-defined function to have its arguments in a scalar context, but that's seldom used, often discouraged, and outside the scope of this tutorial. Most of the time, subs' arguments are in list context.

> Perl's built-ins don't follow this rule at all. Example 10 does not do what's intended.

If that's your first introduction to context you may find it confusing.

From another one I dug up (might have been bot generated, just thought it was funny): Perl has a concept called context. Context means context, and there are the following two contexts.

The second thing, and this may have been more true in the days of yore when I was learning Perl is that there wasn't much consistency in how context was used. Combined with refs, this creates some painful mental overhead when working with multiple libraries in a project. Do I pass credentials as a list, a reference to a list, a specially formatted string, etc? It's not the end of the world but it adds up.


Perl, like Tcl, comes from the Walter Bishop Era of IT, where things were apparently batshit crazy at times but they managed to do things we now fail to replicate :D

Now, much effort is spent in making sure that a single team member cannot get weird ideas and do too much on their own.


Heh. I literally just tabbed away from watching an episode of Fringe[1] to check HN. I didn't expect to walk right into a Walter Bishop reference.

You make a good point though: what do you do with somebody like Walter who is simultaneously A. batshit crazy and B. a genius? I guess the Fringe approach of having people (Astrid, Peter, etc) to "babysit" him most of the time is one approach, but it probably doesn't scale. And one slip-up could result in... well, the end of the world in the fictional case. Hopefully nothing quite so dramatic is at stake here in our real (??) world.

Just don't let the crazy guy keep a cow in the office...

[1]: https://en.wikipedia.org/wiki/Night_of_Desirable_Objects


We must live in the Walternate universe where it takes a million lines of Java to do next to nothing at all.


You're outdated.

Now the new fashion is having a bazillion of YAML files, describing Kubernetes containers full of WASM modules, replicating application servers on the edge.

Apparently it is super cool.


ingy is batshit insane (and great fun to collaborate and/or drink with) but even he didn't expect text templated turing complete YAML to happen.

helm does still (last I checked) allow you to use your own transform script rather than the template engine so for private helm charts you can use https://yglu.io with data in environment variables to get less fragile templating if you can get away with doing it that way.


You're outdated. Today the fashion is to rewrite with millions lines of Rust of which at least 90% are boilerplate to keep the compiler happy.


I find Rust a bit hard to follow (having not studied it in any great detail) but a good deal of that boilerplate either avoids common problems in code or allows you to explicitly say "yes I know I'm doing something dangerous".

However for a while you're right people may have gotten a little bit carried away with "RIIR" - but I cannot fault someone for being enthusiastic :)


Well, when the pair of Rust Witnesses come by in suit and tie to testify to the power of their language and to ask you to move to that language, you're supposed to kidnap them and make them write the program and boring boilerplate for you.


At least you can do Hello world ??


And I often find pleasure in reading tcl/perl communities. So far from the mainstream and trends, yet full of funny surprises.


I recommend to use qx instead of backquotes. Much more readable because with some fonts backquotes are almost invisible.

https://perldoc.perl.org/perlop#qx/STRING/


I use `use IPC::System::Simple` (with `qw/system capture/` where appropriate) whenever I need to call out to a shell - it's the external process equivalent of `use strict`


If you end up doing web development, check out Mojolicious:

https://mojolicious.org/


> such as the ability to embed a shell command in backquotes and get its output easily.

Unless thing have changed in the last 20 years - and they might well have! - that was also a very error-prone tool.

For example, filenames may have spaces in them, but most don't. Experience shows that people forget about that possibility during development and testing. But backticks parse the string after interpolation, causes the spaces to be interpreted as distinct terms.

Here's an example using backticks to call md5sum to compute the MD5 of the empty file "test.txt":

  % touch "test.txt"
  % perl
  $fname = 'test.txt';
  $a = `md5sum $fname`;
  print("Got: $a\n");
  ^D
  Got: 8b8db3dfa426f6bdb1798d578f5239ae  test.txt
Looks good, right? Now try it with "test me.txt":

  % touch "test me.txt"
  % perl
  $fname = 'test me.txt';
  $a = `md5sum $fname`;
  print("Got: $a\n");
  md5sum: test: No such file or directory
  md5sum: me.txt: No such file or directory
This leads to all sort of fun (in the sarcastic sense) when people use variable names with shell metacharacters:

  % touch 'test.txt|echo hello'
  % perl
  $fname = 'test.txt|echo hello';
  $a = `md5sum $fname`;
  print("Got: $a\n");
  Got: hello

There are ways to avoid this, but as the FAQ points out at https://perldoc.perl.org/perlfaq8#How-can-I-call-backticks-w... , the solution is to avoid using backticks. (The example there is of passing an arbitrary search string to grep, without dealing with shell expansion.)

You might be interested to know Julia's take on the same operation. It parses the backtick-quoted string itself (without a shell), and handles interpolation in a shell-like way, with awareness of the underlying variable (eg, string, or enumerating a list or Cartesian product of strings). The result is a Cmd object, which can be passed to other functions to describe how to use or process the Cmd.

See https://docs.julialang.org/en/v1/manual/running-external-pro... .


  use IPC::System::Simple qw(capture);
  
  my $md5sum = capture(md5sum => $fname);
(discovering this exists is the tricky part, of course, but once you know it exists it works nicely)


I'd like to fat comma shame you for this, but I've done the same on occasion, so meh. The thing that irks me about it is that it appears as if the function takes a hash on first read, and I'll happily cargo cult the call style until I finally read the docs for capture, in this case. I don't know, would you put this in prod for other devs to maintain?


I tend to use it consistently to mean "the thing on the left hand side of this is a name", where in this case it's the name of a program.

The commonest case is absolutely the name of a hash key, but it's well worth learning to read it as "is a name of -some- sort" rather than this is a hash because there's a lot of other cases where it's used for different sorts of names, and that's been considered pretty standard for quite a while now.

Examples:

- DBIx::Class column and relationship definitions use it for the name of the thing (and if you generate your DBIx::Class code from the database schema using DBIx::Class::Schema::Loader that's what you're going to get).

- Moo/Moose/Mojo::Base code uses it in has() calls for the name of the attribute, and in before/after/around modifiers for the name of the method being wrapped.

- Mojolicious::Lite and Dancer2 both use it to separate the route name and the route handling subroutine when defining your app's routes.

- Code that can take a method name as the first argument, such as Mojo::Promise, curry.pm, Mojo::Base/Object::Tap's ->tap, and many others, are usually written with the method name followed by => as well.

I could think of more but I think that's quite sufficient to show that it's idiomatic at least in applications perl written using the common greenfield Web Framework(s), Object System(s) and ORM tooling of the time for at least the past 15 years.

I might not put it in production for non-perl-writing -sysadmins- to understand and maintain but for anybody whose job title is developer or who wants to be able to write perl code larger than a single file, I'd much rather teach them how => gets used for names in general since they're almost certainly going to run into it All The Damn Time in documentation, examples, libraries and the application code itself.


> For example, filenames may have spaces in them, but most don't.

This happens in shell scripts as well, and which is why ShellCheck has a warning for it:

* https://www.shellcheck.net/wiki/SC2086

Even with something like find … | xargs … can trip people up if they're not expecting it. It's certainly why GNU (?) added -print0 / -0, but find … -exec … \; works just as well.


That's a bit of a tradeoff though. If you expect that backticks pass things to the shell unchanged, like Perl does, you know what's happening. And as you mention, Perl offers other ways to skip passing to the shell and set args directly.

Julia's approach of parsing things in backticks does remove some footguns, but it also means you have to do things like this:

  julia> run(`echo hello \| sort`);
Else the pipe character is interpreted as a plain argument.


That just echoes "hello | sort", the backtick execution in Julia is only for single commands. For pipelines, there's the `pipeline` function, like so:

      run(pipeline(`echo hello`, `sort`))


Ah, yeah, I had misread the docs there. Though I imagine it creates that unexpected for beginner's thing where `ls | sort` comes back with "no such file |" or similar. Basically, I'm saying there is a place for an interface that does pass a single string to the shell, and an interface where you specify args explicitly.


FYI one of the things I like about Julia is that it also can execute shell commands via backquote syntax, in a very nice way (e.g. interpolating arrays just works). E.g. try

    files = split(read(`ls`, String))
    firsttwo = files[1:2]
    run(`ls -l $firsttwo`)
and this does the right thing even if you have a filename with spaces in it. or this


> I'm learning perl right now and I'm bewildered at how could I overlook it for such a long time.

perl is really great at what it's great at. I say that's anything with a substantial need for text manipulation and regex handling and/or anything with a lot of interaction with other CLI tools. There is no other language that makes such tasks as quick and painless as perl. It's a wonderful tool in the toolbox. Not one I use most often but when it's needed, it's awesome to have.


It's an odd critter, but it gets the job done. There are a few pitfalls, such as list flattening, but in my experience, once you are aware of them, they are not hard to avoid.

If you are new to Perl, I highly recommend reading Damien Conway's Perl Best Practices. Even if you end up not picking up all the suggestions, it is worth thinking about them and the problems they try to address.


  the ability to embed a shell command in backquotes and get its output easily
Aside from that being a gigantic footgun, you can do this in Ruby, PHP, and in POSIX shells but not in Javascript or Python.


Never knew you can do it in Ruby too, thanks for sharing. I’ve missed that from python a lot.


So I firmly believe that using shell expansion in your scripts is a horrid idea no matter what the syntax. But while you're thinking about backticks and ruby:

Much like a modern POSIX shell you can also use the more ergonomic %x (e.g. %x{echo true} %x(echo false).

Backticks will perform string interpolation before execution just like a normal double quoted string.

Backticks are simply a method, so you can redefine that on any object.


Not sure why you think python can’t do this. I do it all the time.


I'm reasonably sure you don't "embed a shell command in backquotes" all the time in Python (and I don't think this is pedantry; the specific ease of this syntax was the claimed benefit, not just the ability to shell out in some way).

Backticks were an alias of `repr` in Python 2.x:

    $ python2.7
    Python 2.7.6 (default, Nov 13 2018, 12:45:42) 
    [GCC 4.8.4] on linux2
    Type "help", "copyright", "credits" or "license" for more information.
    >>> foo="this is foo"
    >>> `foo`
    "'this is foo'"
And it went away in 3:

    $ python3
    Python 3.11.3 (main, Apr  5 2023, 15:52:25) [GCC 12.2.1 20230201] on linux
    Type "help", "copyright", "credits" or "license" for more information.
    >>> `ls`
      File "<stdin>", line 1
        `ls`
        ^
    SyntaxError: invalid syntax


How? I only know of subprocess.


Doubtful.


Do make sure that 100% of the input is from trusted sources because if not, writing secure Perl programs is extremely hard. The tainting system helps but doesn’t solve the problem.


Taint mode is something I've never got along with, but for running external programs p3rl.org/IPC::System::Simple lets you make all the shell injection risks go away (and can be used in a script destined for p3rl.org/App::FatPacker so you're still deploying a single file).

Writing secure anything is extremely hard, but 'avoid backticks, single-arg system, and 2-arg open like the plague' tends to reduce the hardness in perl back to something reasonable similar to the hardness of doing it in other languages.

(at least it's not shell ... arghsob)




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: