tags:

views:

449

answers:

7

Do you think changing directories inside bash or Perl scripts is acceptable? Or should one avoid doing this at all costs?

What is the best practice for this issue?

+3  A: 

I don't do this often, but sometimes it can save quite a bit of headache. Just be sure that if you change directories, you always change back to the directory you started from. Otherwise, changing code paths could leave the application somewhere it should not be.

Ovid
Not on Unix derivatives, it couldn't.
Jonathan Leffler
Yes it can if you take "application" as "the same executable, but different functions" - you just can't change the parent process' cwd.
Tanktalus
What kind of an interpretation of the word "application" would lead you to think that??
ephemient
@Tanktalus had the interpretation I meant, even if I apparently explained it poorly. In an application, if you go around changing directories several times and you alter your code paths, different parts of the application could be expecting different starting directory points.
Ovid
+13  A: 

The current working directory is local to the executing shell, so you can't affect the user unless he is "dotting" (running it in the current shell, as opposed to running it normally creating a new shell process) your script.

A very good way of doing this is to use subshells, which i often do in aliases.

alias build-product1='(cd $working-copy/delivery; mvn package;)'

The paranthesis will make sure that the command is executed from a sub-shell, and thus will not affect the working directory of my shell. Also it will not affect the last-working-directory, so cd -; works as expected.

Hugo
+1  A: 

Consider also that Unix and Windows have a built in directory stack: pushd and popd. It’s extremely easy to use.

Jon Shea
+9  A: 

Like Hugo said, you can't effect your parent process's cwd so there's no problem.

Where the question is more applicable is if you don't control the whole process, like in a subroutine or module. In those cases you want to exit the subroutine in the same directory as you entered, otherwise subtle action-at-a-distance creeps in which causes bugs.

You can to this by hand...

use Cwd;
sub foo {
    my $orig_cwd = cwd;
    chdir "some/dir";

    ...do some work...

    chdir $orig_cwd;
}

but that has problems. If the subroutine returns early or dies (and the exception is trapped) your code will still be in some/dir. Also, the chdirs might fail and you have to remember to check each use. Bleh.

Fortunately, there's a couple modules to make this easier. File::pushd is one, but I prefer File::chdir.

use File::chdir;
sub foo {
    local $CWD = 'some/dir';

    ...do some work...
}

File::chdir makes changing directories into assigning to $CWD. And you can localize $CWD so it will reset at the end of your scope, no matter what. It also automatically checks if the chdir succeeds and throws an exception otherwise. Sometimes it use it in scripts because it's just so convenient.

Schwern
+1  A: 

For Perl, you have the File::pushd module from CPAN which makes locally changing the working directory quite elegant. Quoting the synopsis:

  use File::pushd;

  chdir $ENV{HOME};

  # change directory again for a limited scope
  {
      my $dir = pushd( '/tmp' );
      # working directory changed to /tmp
  }
  # working directory has reverted to $ENV{HOME}

  # tempd() is equivalent to pushd( File::Temp::tempdir )
  {
      my $dir = tempd();
  }

  # object stringifies naturally as an absolute path
  {
     my $dir = pushd( '/tmp' );
     my $filename = File::Spec->catfile( $dir, "somefile.txt" );
     # gives /tmp/somefile.txt
  }
tsee
+2  A: 

I'll second Schwern's and Hugo's comments above. Note Schwern's caution about returning to the original directory in the event of an unexpected exit. He provided appropriate Perl code to handle that. I'll point out the shell (Bash, Korn, Bourne) trap command.

trap "cd $saved_dir" 0

will return to saved_dir on subshell exit (if you're .'ing the file).

mike

mpez0
A: 

Is it at all feasible to try and use fully-quantified paths, and not make any assumptions on which directory you're currently in? e.g.

use FileHandle;
use FindBin qw($Bin);
# ...
my $file = new FileHandle("< $Bin/somefile");

rather than

use FileHandle;
# ...
my $file = new FileHandle("< somefile");

This will probably be easier in the long run, as you don't have to worry about weird things happening (your script dying or being killed before it could put the current working directory back to where it was), and is quite possibly more portable.

Sam Kington