# Extracting parts of filenames from an array

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;

foreach(@configs)
{
$_ =~ 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

exit;

• 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. – Borodin Aug 16 '17 at 21:18

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$LIST_SEPARATOR
$" 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.

# 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";  Output: m4/config.a2.m4 m4/config.mk.m4 m4/config.php.m4 config.a2 config.mk config.php  • 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) – mpapec Aug 15 '17 at 16:46
• @mpapec: True but if it doesn't match, I guess it's correct to return nothing. – Toto Aug 15 '17 at 16:57
• Yes, but in your case map will always return $1, regardless of unsuccessful match. – mpapec Aug 15 '17 at 17:03 • @mpapec: Ok, I see. corrected. – Toto Aug 15 '17 at 17:06 • Wow, this is pure wizardry. Great work. – Sanchke Dellowar Aug 15 '17 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 foreach(@configs) {$_ =~ 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  m{m4/(config\..*)\.m4};  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. • 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. – Borodin Aug 16 '17 at 21:47