Dave Ferrero
Dave Ferrero

Reputation: 11

Perl - How to modify variables with subroutines without return

The next code is used to get a filepath and check if exists and can be read; otherwise the value will be switched to a custom one:

use strict;
use warnings;

[...]

sub checkFilePath{
    my ($args) = @_;

    my $checkingPath = $args->{path};
    my $checkingCustomPath = $args->{customPath};

    my $canBeRead = 1;
    if ($checkingPath) {
        if (!(-e "$checkingPath")) {
            print "[WARN] File $checkingPath doesn't exist.\n";
            $canBeRead = 0;
        } elsif (!(-f "$checkingPath")) {
            print "[WARN] $checkingPath is not a file.\n";
            $canBeRead = 0;
        } elsif (!(-r "$checkingPath")) {
            print "[WARN] File $checkingPath can't be read.\n";
            $canBeRead = 0;
        }
    } 
    if (!($canBeRead)) {
        # Testing custom regex file path
        # If doesn't exist, it will try to use custom file or the script will die
        die "[ERR] Custom file $checkingCustomPath doesn't exist\n"  if (!(-e $checkingCustomPath));
        die "[ERR] Custom file $checkingCustomPath is not a file\n"  if (!(-f $checkingCustomPath));
        die "[ERR] Custom file $checkingCustomPath cannot be read\n" if (!(-r $checkingCustomPath));
        return $checkingCustomPath;
    }
    return $checkingPath;
}

[...]

$logPath = checkFilePath({
    path => $logPath,
    customPath => $customLogPath
    });

I was wondering if there is a way to modify this code to update $logPath only with a subroutine call, like:

# $logPath = '/tmp/thisfiledoesntexist.txt'
checkFilePath({
        path => $logPath,
        customPath => $customLogPath
        });
# $logPath now has a valid filepath, which is the same as $customLogPath

Upvotes: 1

Views: 207

Answers (2)

Sinan Ünür
Sinan Ünür

Reputation: 118156

Thinking about this a little more, I decided to propose a different, less repetitive, and, IMO, clearer way of doing it:

use strict;
use warnings;

use autouse Carp => qw(croak);

print chooseFilePath('doesnot.exist', "$ENV{TEMP}/t.log"), "\n";

sub chooseFilePath {
    my $wantedPath = shift;
    my $defaultPath = shift;

    if (defined(my $reason = isBadFilePath($wantedPath))) {
        warn "[WARN] $reason.\n";
        if (defined($reason = isBadFilePath($defaultPath))) {
            die "[ERR] $reason.\n";
        }
        return $defaultPath;
    }

    return $wantedPath;
}

sub isBadFilePath {
    @_ or croak 'Need a path';

    my $path = shift;

    -e $path or return "File '$path' doesn't exist";
    -f _ or return "'$path' is not a file";
    -r _ or return "File '$path' can't be read";

    return;
}

Output:

C:\...\Temp> perl s.pl
[WARN] File 'doesnot.exist' doesn't exist.
[ERR] File 'C:\...\Temp/t.log' doesn't exist.

C:\...\Temp> echo x > t.log

C:\...\Temp> perl s.pl
[WARN] File 'doesnot.exist' doesn't exist.
C:\...\Temp/t.log

Upvotes: 1

ikegami
ikegami

Reputation: 386541

If $logPath was passed to the subroutine as an argument (or via a reference), it would be possible to change it (by modifying the correct element of @_ (or modifying the referenced scalar)). But you copy its value into a hash and pass a reference to that hash instead. At best, you could modify $hash->{path} instead of $logPath.

sub fixFilePath {
    our $checkingPath; local *checkingPath = \shift;  # my \$checkingPath = \shift;
    my %args = @_;
    my $checkingCustomPath = $args{customPath};

    ...

    return if $canBeRead;

    ...

    $checkingPath = $checkingCustomPath;
}


fixFilePath($logPath,
    customPath => $customLogPath,
);

Upvotes: 1

Related Questions