Beefy Boxes and Bandwidth Generously Provided by pair Networks
Come for the quick hacks, stay for the epiphanies.
 
PerlMonks  

Re: count multiple variables in a single array.

by Marshall (Canon)
on Feb 27, 2020 at 06:30 UTC ( [id://11113474]=note: print w/replies, xml ) Need Help??


in reply to count multiple variables in a single array.

It is a bit hard for me to understand your complete specification. However, here is an attempt and some advice.

I would not as a first thought change into the directory (chdir). Instead, I would run glob on the target directory from the current program directory. You have the full path information to do that. The problem with chdir is that you have to keep track of "where you are". In a longer program this can become problematic, especially in the case of some path error.

You seem to have a file naming convention for files in this error directory with "userName-fileName.extn". I would not overly specify the .extns that could be in this directory. I allow any extension below (tiff, psd, whatever).

I would of course not "hard code" the user names, which I think is part of your question. I use a HoA (Hash of Array) below. I made a few files underneath a test directory on my Windows machine, keyed to each discovered user name.

A long time ago, I would have recommended against using glob() because there were 3 distinct versions and it wouldn't be clear which version you have. Now things have become more standardized and I think glob() is fine.

use strict; use warnings; use Data::Dumper; my $path ='c:/test'; my %files_per_user; #HOA foreach my $file (glob("$path/*")) { next unless -f $file; # skip directories # if any exist my ($user,$detail_name) = split ('-',$file,2); push @{$files_per_user{$user}},$detail_name; } print Dumper \%files_per_user; foreach my $user (sort keys %files_per_user) { print "",(split("/",$user))[-1],"\n"; print " $_\n" for @{$files_per_user{$user}}; } __END__ What the TEST Directory looks like: Directory of C:\test 02/26/2020 09:52 PM <DIR> . 02/26/2020 09:52 PM <DIR> .. 02/26/2020 09:22 PM 0 maur-1110.tiff 02/26/2020 09:21 PM 0 maur-1111.psd 08/05/2019 01:14 AM <DIR> subdirtest 02/26/2020 09:20 PM 0 TUMI-1354839054_alt1_.psd 3 File(s) 0 bytes $VAR1 = { 'c:/test/TUMI' => [ '1354839054_alt1_.psd' ], 'c:/test/maur' => [ '1110.tiff', '1111.psd' ] }; TUMI 1354839054_alt1_.psd maur 1110.tiff 1111.psd

Replies are listed 'Best First'.
Re^2: count multiple variables in a single array.
by flieckster (Scribe) on Feb 28, 2020 at 18:41 UTC
    Thank you, i think your correct in changing directories it does get very confusing, but sometimes a necessary evil, but i do like the approach. this does seem to work best just to dice up the filenames into prefix and filenames. ultimately this all is going to be sent using  use Email::Send::SMTP::Gmail; what would be your advice for the best way to catch the output of the print commands fo storage to become part of the body of the email?
      It's a good idea to separate the output from the logic if you can, especially if that output is HTML. Consider this:
      use Modern::Perl; use Email::Send::SMTP::Gmail; use POSIX qw(strftime); use File::Find::Rule; use Template; my $subject = "ICS -- Files in the Error Folder"; my $email = 'bflieck@xxx.com'; my @bad_files = File::Find::Rule ->file() ->relative ->maxdepth( 1 ) ->name( '*.jpg', '*.tif', '*.tiff', '*.psd' ) ->in( '/Volumes/photorepos/Partners/WorkHorse/ERROR/PhotographyDro +p_ERROR' ); # Group files per user my %bad_files_per_user; foreach my $file ( @bad_files ) { my $user = (split /-/, $file)[0]; $bad_files_per_user{ $user } = $file; } # Prepare the email text my $body = Template->new->process( \*DATA, { subject => $subject, files => \%bad_files_per_user, date => strftime( "%m-%d-%y", localtime ), time => strftime( "%I:%M:%S", localtime ), }); # Prepare sender my ( $sender, $error ) = Email::Send::SMTP::Gmail->new ( -subject => $subject, -to => $email, -from => $email, -body => $body, # Better to put these in some kind of configuration file # -debug => 1, -smtp => 'smtp.gmail.com', -login => 'kopautomation1@xxx.com', -pass => 'xxx', -layer => 'ssl', -port => '465', -timeout => 1000 ); die "session error: $error" unless ref $sender; # Off we go $sender->send; $sender->bye; __DATA__ <h1>[% subject %]</h1> <p class="date">[% date %] - [% time %]</p> <p>Please fix and redrop or delete the following errors</p> <div class="bad_format"> <h2>Files with bad format</h2> [% FOREACH user IN files.keys %] <h3>User: [% user %] ([% files.$user.size %] files)</h3> <ul class="files"> [% FOREACH file IN files.$user %] <li>[% file %]</li> [% END %] </ul> [% END %] </div>
      I am using the Template Toolkit and a template stored in the __DATA__ section of the script to generate the mail. Because the output is now separated from the code, I (you) can make the email arbitrarily complex (like adding images or styles or whatnot) without having to change or clutter the the code with print statement.

      Also, I am sure you know this already, whitespace is <s>cheap</s> free!


      holli

      You can lead your users to water, but alas, you cannot drown them.
      …changing directories it does get very confusing, but sometimes a necessary evil…

      I am open to being shown incorrect but I don’t think it’s ever necessary and in every case I have come across it so far—including some of my own older stuff—I find it to be code smell.

        I equivocated in my post to the OP.
        I also don't see any "necessary evil"
        Some Perl libraries like File::Find do chdir's for their work and error recovery from a something like that can be very problematic because you can wind up in some random directory. The &wanted routine should be as simple as possible to avoid this problem.
        In general, chdir is a bad idea.
        I could think of situations where a chdir to a program's data could simplify the code slightly, but I also don't see "necessary evil", meaning "required".
      Yes, chdir() does have its place.

      "advice for the best way to catch the output of the print commands fo storage to become part of the body of the email?
      You could use sprintf() which makes a string?

      I am not sure whether these user names like "Maur" are actual users on your unix machine? If so, I would use simple SendMail to that user ID and leave it to the users to have a .forward file in their account that forwards to gmail. Don't create a mapping file of some userIDname to gmail address unless for some reason you have to. This adds an extra layer of admin hassle for you.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://11113474]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others surveying the Monastery: (5)
As of 2024-04-23 20:58 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found