http://www.perlmonks.org?node_id=91719

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

Is there a way to do what I mean:
if ($foo) { local $cwd = ".."; /* current working directory changes */ ... } /* cwd is back to original directory */


The semantics being:
if ($foo) { my $old_cwd = cwd; chdir ".."; ... chdir $old_cwd; }

Replies are listed 'Best First'.
Re: local scoped cwd
by Abigail (Deacon) on Jun 27, 2001 at 03:02 UTC
    #!/opt/perl/bin/perl -w use strict; sub TIESCALAR {chomp (my $cwd = `pwd`); bless \$cwd => shift} sub FETCH {${$_[0]}} sub STORE {chdir $_[1] or die; ${$_[0]} = $_[1]} use vars qw /$cwd/; tie $cwd => 'main';
    Now you can just assign to $cwd to change directories, and when you localize it, it will restore the directory when the value goes out of scope.

    -- Abigail

      Non-Unix users take note. You can use the Cwd module instead of chomping `pwd` to make the above portable.

      That said I think the above tie implementation would be a nice addition to the Cwd module.

      UPDATE
      Shouldn't the FETCH method return the real cwd and not just the last thing it was set to through the tied variable?

Re: local scoped cwd
by bikeNomad (Priest) on Jun 26, 2001 at 23:40 UTC
    How about this OO approach:

    #!/usr/bin/perl -w use strict; package MyCwd; use Cwd; sub cd { warn "must assign to something!\n" if !defined(wantarray); my $class = shift; my $new_dir = shift; my $self = cwd; chdir $new_dir; bless(\$self, $class); } sub DESTROY { my $self = shift; chdir $$self; } "that's it!";

    And then you'd use it like this:

    package main; use MyCwd; use Cwd; print cwd, "\n"; if (1) { my $cwd = MyCwd->cd('..'); { my $cwd = MyCwd->cd('..'); print cwd, "\n"; ] print cwd, "\n"; } print cwd, "\n";

    Every time you construct a new MyCwd, it remembers where you were. When it goes out of scope, it changes back.

    update: Separated demo code. Added check for call in void context.

Re (tilly) 1: local scoped cwd
by tilly (Archbishop) on Jun 27, 2001 at 00:11 UTC
    My ReleaseAction module offers the following example that I think does exactly what you want:
    use Carp; use Cwd; use ReleaseAction; sub cd_to { chdir($_[0]) or confess("Cannot chdir to $_[0]: $!"); } sub tmp_cd { my $cwd = cwd(); cd_to(shift); ReleaseAction->new(\&cd_to, $cwd); } sub something_interesting { my $in_dir = tmp_cd("some_dir"); # Do something interesting in the new dir # I will automagically return to the old dir # when I exit the subroutine and $in_dir goes # out of scope. }
Re: local scoped cwd
by mugwumpjism (Hermit) on Jun 27, 2001 at 16:50 UTC

    Just thought I'd point out that it is not always possible to return to the directory you were in... so depending on your requirements, you might be better off doing something like:

    if (fork()) { wait; } else { chdir ("/trap"); # code here - note: you will need to use IPC or some # other mechanism to get results back to the parent # process. }
Re: local scoped cwd
by Anonymous Monk on Jun 28, 2001 at 04:03 UTC
    in summary:
    use Cwd; sub TIESCALAR {bless {} => shift} sub FETCH {cwd} sub STORE {defined $_[1] and (chdir $_[1] or die $!)} our $cwd; tie $cwd => __PACKAGE__;
    that defined $_[1] is necessary becuase local seems to undef the variable at some point (otherwise resulting in a chdir to $HOME).
      If you aren't going to use the blessed var, why bother with the overhead and size of a hash? Why not:
      sub TIESCALAR {bless \(my $dummy) => shift}

        I guess I was just playing golf. :)

        How well would { bless \$_ => shift } work?