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

Hi Monks,

Please be gentle, I am new to Perl having picked up some scripts from a former colleague, however I am banging my head against the wall from something I feel should be rather simple.

So, I have a directory with a number of files, only one of those files have the letters '_CF_' in the filename in that order. I need to set a variable $ScontrolFile as the filename containing '_CF_'.

I thought the way to go was via an Array, I managed to read the files in from the Directory, and grep the entries to confirm the file I want is there, I can't seem to set the variable though.

Am I heading in the right direction or over complicating it?

Any hints, tricks or solutions will be greatly received.

Thanks

Replies are listed 'Best First'.
Re: Setting a Variable from filename
by haukex (Archbishop) on Apr 29, 2018 at 12:40 UTC
    I thought the way to go was via an Array, I managed to read the files in from the Directory, and grep the entries to confirm the file I want is there, I can't seem to set the variable though.

    As Corion said, it would be good if you could show the code, best would be as a Short, Self-Contained, Correct Example (see also How do I post a question effectively?) - for example, it can make a significant difference whether the files include the pathname or not.

    But if I were to take a guess that the pathname is not included in the filename, you probably already have code something like my @wanted = grep {/_CF_/} @files;? In that case, you should be able to get the filename via $ScontrolFile = $wanted[0]; - but first, you might want to make sure that your grep did indeed match exactly one file by saying die "did not find exactly one _CF_ file (found: @wanted)" unless @wanted==1;.

      Apologies, my current code is in work, however your response nails it for me I think, I hadn't considered using grep into a 2nd array where I then know the index will be [0].

      Thanks for the response.

        As always, There Is More Than One Way To Do It. If you're certain that the grep condition will always match exactly one element of @files:

        • $ScontrolFile = (grep {/_CF_/} @files)[0]; - create a "temporary" list and get its first element
        • ($ScontrolFile) = grep {/_CF_/} @files; - use assignment in list context
        • use List::Util qw/first/; $ScontrolFile = first {/_CF_/} @files; - use first from the core module List::Util, which has the advantage that it stops looking once it finds a match

        However, none of the above solutions provide protection against the case that @files does not contain exactly one string matching the pattern, which is why I suggested the extra array. <update> AnomalousMonk showed a way to add the check to the second of my examples above. To add a check to the other two, </update> you could theoretically search the array a second time, using grep in scalar context (if ( grep({/_CF_/} @files)!=1 ) { die ... }), but that's fairly wasteful and you'll have duplicated code (the grep block). Of course, you can also write an explicit loop:

        my $ScontrolFile; for my $file (@files) { if ( $file =~ /_CF_/ ) { die "more than one _CF_ file found in: @files" if defined $ScontrolFile; $ScontrolFile = $file; } } die "_CF_ file not found in: @files" unless defined $ScontrolFile;

        Update: Added check for >1 file in the above code, and see update in text.

        Well, you could grep your values into the same array if you wished:
        @files = grep { /_CF_/ } @files;
        Now, @files will contain only the file(s) matching the regex.

        But it is probably clearer (and easier to debug if you need) to grep the matching values into a new array and leave the original one untouched.

        Yet Another Way to capture first matching file, if any, and number of matching files concisely:

        c:\@Work\Perl\monks>perl -wMstrict -le "my @files = qw(xxx 1st_CF_xx CF__ 2nd_CF_yy CF); ;; my $n_CF_ = my ($first_CF_) = grep /_CF_/, @files; ;; print qq{number of _CF_ files: $n_CF_}; if ($n_CF_ == 1) { print qq{just one: '$first_CF_'}; } " number of _CF_ files: 2 c:\@Work\Perl\monks>perl -wMstrict -le "my @files = qw(xxx 1st_CF_xx CF__ CF); ;; my $n_CF_ = my ($first_CF_) = grep /_CF_/, @files; ;; print qq{number of _CF_ files: $n_CF_}; if ($n_CF_ == 1) { print qq{just one: '$first_CF_'}; } " number of _CF_ files: 1 just one: '1st_CF_xx' c:\@Work\Perl\monks>perl -wMstrict -le "my @files = qw(xxx yyy zzz); ;; my $n_CF_ = my ($first_CF_) = grep /_CF_/, @files; ;; print qq{number of _CF_ files: $n_CF_}; if ($n_CF_ == 1) { print qq{just one: '$first_CF_'}; } " number of _CF_ files: 0

        Update: Be aware that if no files match the grep condition in the example code above,  $first_CF_ is undefined.


        Give a man a fish:  <%-{-{-{-<

Re: Setting a Variable from filename
by Corion (Patriarch) on Apr 29, 2018 at 12:21 UTC

    Can you show us the relevant code you have and how it fails to do what you want?

    I would imagine that something like the following should work for your code, but that's mostly guesswork from your description and not based on seeing actual code:

    my @filenames = qw(foo_CF_foo bar_CF_bar baz_NOCF_baz); my $file = $filenames[0];