Как лучше всего менять рабочие каталоги внутри скриптов? - PullRequest
11 голосов
/ 27 октября 2008

Как вы думаете, допустимо ли изменение каталогов внутри скриптов bash или Perl? Или следует избегать этого любой ценой?

Какова лучшая практика для этой проблемы?

Ответы [ 7 ]

26 голосов
/ 28 октября 2008

Как сказал Хьюго, вы не можете воздействовать на cwd вашего родительского процесса, поэтому проблем нет.

Где вопрос более применим, если вы не контролируете весь процесс, как в подпрограмме или модуле. В этих случаях вы хотите выйти из подпрограммы в том же каталоге, в котором вы ввели, в противном случае незаметное действие происходит на расстоянии, что приводит к ошибкам.

Вы можете сделать это вручную ...

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

    ...do some work...

    chdir $orig_cwd;
}

но с этим есть проблемы. Если подпрограмма возвращается рано или умирает (и исключение перехватывается), ваш код все равно будет в some/dir. Кроме того, chdir могут не работать, и вы должны помнить, чтобы проверить каждое использование. BLEH.

К счастью, есть пара модулей, чтобы сделать это проще. File :: pushd один, но я предпочитаю File :: chdir .

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

    ...do some work...
}

File :: chdir переводит каталоги в $CWD. И вы можете локализовать $CWD, чтобы он сбрасывался в конце вашей области, несмотря ни на что. Он также автоматически проверяет, успешно ли chdir, и выдает исключение в противном случае. Иногда это используется в скриптах, потому что это так удобно.

16 голосов
/ 27 октября 2008

Текущий рабочий каталог является локальным по отношению к исполняющей оболочке, поэтому вы не можете повлиять на пользователя, если он не «ставит точки» (запускает его в текущей оболочке, в отличие от запуска, обычно создающего новый процесс оболочки) вашего сценария .

Очень хороший способ сделать это - использовать подоболочки, что я часто делаю в псевдонимах.

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

В результате проверки будет выполнено выполнение команды из вложенной оболочки, что не повлияет на рабочий каталог моей оболочки. Также это не повлияет на последний рабочий каталог, поэтому cd -; работает как положено.

4 голосов
/ 28 октября 2008

Для Perl у вас есть модуль File :: pushd из CPAN, который делает локальное изменение рабочего каталога довольно элегантным. Цитирую синопсис:

  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
  }
4 голосов
/ 27 октября 2008

Я делаю это не часто, но иногда это избавляет от головной боли. Просто убедитесь, что если вы меняете каталоги, вы всегда возвращаетесь к каталогу, из которого вы начали. В противном случае изменение пути к коду может оставить приложение где-то, где это не должно быть.

3 голосов
/ 28 октября 2008

Я поддержу комментарии Шверна и Хьюго выше. Обратите внимание на предостережение Шверна о возвращении в исходный каталог в случае неожиданного выхода. Он предоставил соответствующий код Perl, чтобы справиться с этим. Я укажу команду ловушки оболочки (Bash, Korn, Bourne).

trap "cd $ save_dir" 0

вернется к Saved_dir при выходе из подоболочки (если вы используете файл).

1007 * микрофон *

1 голос
/ 28 октября 2008

Учтите также, что Unix и Windows имеют встроенный стек каталогов: pushd и popd . Он чрезвычайно прост в использовании.

0 голосов
/ 28 октября 2008

Можно ли вообще использовать полностью квантифицированные пути и не делать никаких предположений о том, в каком каталоге вы сейчас находитесь? например,

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

вместо

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

Это, вероятно, будет легче в долгосрочной перспективе, так как вам не нужно беспокоиться о странных вещах (ваш скрипт умирает или убивается до того, как он может вернуть текущий рабочий каталог обратно туда, где он был), и это довольно возможно более портативный.

...