I am very new to perl. So far I love it, my new favorite interpreted language. I am quickly learning that perl has MANY 'tricks' to it. The following code extracts file names out of one array and into another using a simple regex. Is there a shorter way of doing this?

It feels like there's a way to remove the for loop.

my @configs = glob("m4/config.*.m4");
my @outputs;

    $_ =~ m/m4\/(config\..*)\.m4/;
    push @outputs, $1;

print join(' ',@configs) . "\n";
# m4/config.a2.m4 m4/config.mk.m4 m4/config.php.m4

print join(' ',@outputs) . "\n";
# config.a2 config.mk config.php

  • \$\begingroup\$ It's worrying that you think of any parts of Perl as being a "trick". Some facilities, such as built-in regex patterns, may be unfamiliar to you, but if you really think that something is a "trick" in any way then you should consider coding it differently. \$\endgroup\$
    – Borodin
    Aug 16, 2017 at 21:18

2 Answers 2


As said before, use map instead of a foreach loop and change the regex delimiter.

If you put the array in double quote when print, a space is inserted between each element of the array in fact it is the content of the special variable $", see http://perldoc.perl.org/perlvar.html


When an array or an array slice is interpolated into a double-quoted string or a similar context such as /.../ , its elements are separated by this value. Default is a space. For example, this:

   print "The array is: @array\n";

is equivalent to this:

   print "The array is: " . join($", @array) . "\n";

Mnemonic: works in double-quoted context.

Your code becomes:

# Use ALLWAYS these two lines in every script.
use strict;
use warnings;

my @configs = glob("m4/config.*.m4");
my @outputs = map{ m~m4/(config\..*)\.m4$~ } @configs;
print "@configs\n";
print "@outputs\n";


m4/config.a2.m4 m4/config.mk.m4 m4/config.php.m4
config.a2 config.mk config.php
  • \$\begingroup\$ my @outputs = map{ m~m4/(config\..*)\.m4$~ } @configs; will give empty list in case regex doesn't match (even when you're sure it always will) \$\endgroup\$
    – mpapec
    Aug 15, 2017 at 16:46
  • \$\begingroup\$ @mpapec: True but if it doesn't match, I guess it's correct to return nothing. \$\endgroup\$
    – Toto
    Aug 15, 2017 at 16:57
  • \$\begingroup\$ Yes, but in your case map will always return $1, regardless of unsuccessful match. \$\endgroup\$
    – mpapec
    Aug 15, 2017 at 17:03
  • \$\begingroup\$ @mpapec: Ok, I see. corrected. \$\endgroup\$
    – Toto
    Aug 15, 2017 at 17:06
  • \$\begingroup\$ Wow, this is pure wizardry. Great work. \$\endgroup\$
    – Dellowar
    Aug 15, 2017 at 22:10

The Perl command that you want is map. I don't have time to work up a test, so I won't try to write the code. You may find this Stack Overflow relevant, although it is doing something slightly different.

A few other quick comments

    $_ =~ m/m4\/(config\..*)\.m4/;

A more common way to write this is

foreach my $config (@configs)
    $config =~ m{m4/(config\..*)\.m4};

Because the regex contains a /, you don't want to use that as the delimiter. I prefer the {} delimiters. Perl will take almost anything though. E.g. # instead of / is one common alternative.

I prefer to declare a variable rather than use the default $_.

Note that


is the same as saying

    $_ =~ m{m4/(config\..*)\.m4};

The $_ is the default both for the foreach and for the regular expression. So you normally wouldn't write it out here. If you want to be extra explicit, declaring the variable with my is even better.

  • \$\begingroup\$ I very much disagree. Perl provides $_ for precisely this purpose, and there is no need for the noise of declaring and comparing $config. There is also no need to write foreach when for is identical. \$\endgroup\$
    – Borodin
    Aug 16, 2017 at 21:47

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.