在运行Node脚本时,更改当前shell上下文中的工作目录

148

当我的Node.js脚本从bin脚本运行时,我希望更改工作目录。我有类似以下的内容:

#!/usr/bin/env node
process.chdir('/Users')
当我用 ./bin/nodefile 运行这个文件时,它退出了,但是当前 shell 上下文的工作目录并没有改变。我也尝试过 shelljs,但那也不起作用。
最好的方法是什么?我知道它是在一个单独的进程中工作的。

1
你有什么证据表明当前工作目录(cwd)没有改变?请记住,require()中使用的路径和cwd是两个不同的东西。 - jeesty
有没有在Windows中实现这个的方法? - Phillip
如果你想通过执行nodejs脚本来更改目录,那是行不通的。我猜,它和bash一样在子shell中运行,无法更改父shell的工作目录。对于bash,可以通过例如source脚本或在脚本末尾运行$SHELL或exec bash来解决。但是对于nodejs,我不知道该怎么做。 - Dmitry Sheiko
每个人都应该听从 @jeesty 的建议:即使您已更改此工作目录,但使用 require("fs") 进行操作时,工作目录不会受到影响。 - Yan King Yin
7个回答

186

改变目录的正确方式实际上是使用 process.chdir(directory)。以下是来自文档的示例:

console.log('Starting directory: ' + process.cwd());
try {
  process.chdir('/tmp');
  console.log('New directory: ' + process.cwd());
}
catch (err) {
  console.log('chdir: ' + err);
}

这也可以在Node.js REPL中进行测试:

[monitor@s2 ~]$ node
> process.cwd()
'/home/monitor'
> process.chdir('../');
undefined
> process.cwd();
'/home'

哎呀,是的,那确实是我正在使用的命令。然而,当我在一个简单的脚本中使用它时,它似乎仍然不起作用(一旦脚本退出,我仍然停留在旧目录中)。如果我调用process.cwd(),它会显示我应该在的目录,但是如果我调用pwd,则终端会显示我在其他地方。因为这是一个不同的进程? - Jonovono
13
该方法改变进程的当前工作目录,而不是shell的。据我所知,更改正在运行进程的外部cwd非常复杂且不推荐。您想要这样做的原因是什么? (翻译提示:将句子结构调整为主语+谓语+宾语的形式,并将重点单词进行翻译,同时保持原文意思不变。) - hexacyanide
5
好的,谢谢。我想这样做的原因是我正在制作一个终端应用程序来组织我的项目。基本上,我希望能够列出我的项目并轻松地将当前的shell位置更改为该位置。现在我想想,我要看一下z(https://github.com/rupa/z)如何实现这一点。虽然那是在bash中完成的。我或许可以用applescript来做到这一点? - Jonovono
@hexacyanide 感谢您的添加,因为更改没有对齐,所以一直困扰着我。 - EugenSunic

38

在Node中没有内置的方法可以改变运行Node进程的底层shell的当前工作目录(CWD)。

可以通过命令process.chdir()更改Node进程的当前工作目录。

var process = require('process');
process.chdir('../');
当Node进程退出时,你会发现自己回到了启动该进程的当前工作目录。

20
你想要做的事情是不可能的。这是因为在一个POSIX系统(Linux,OSX等)中,子进程不能修改父进程的环境。这包括修改父进程的工作目录和环境变量。
当你在命令行上执行Node脚本时,你当前的进程(bash,zsh等)会生成一个新的进程,该进程具有自己的环境,通常是你当前环境的副本(通过系统调用可以更改此设置; 但这超出了此回复的范围),允许该进程在完全隔离的情况下执行所需的任何操作。当子进程退出时,控制权返回到你的shell进程,其中环境没有受到影响。
这有很多原因,但其中之一是,想象一下你在后台执行脚本(通过./foo.js &)并且正在运行时,它开始更改你的工作目录或覆盖你的PATH。那将是一场噩梦。
如果您需要执行一些需要更改外壳工作目录的操作,则需要在您的shell中编写一个函数。例如,如果您正在运行Bash,则可以将以下内容放入您的~/.bash_profile:
do_cool_thing() {
  cd "/Users"
  echo "Hey, I'm in $PWD"
}

然后这个酷炫的东西是可以实现的:

$ pwd
/Users/spike
$ do_cool_thing
Hey, I'm in /Users
$ pwd
/Users

如果你需要进行更复杂的操作,你可以随时从该函数中调用你的nodejs脚本。

这是你能够完成你所要做的事情的唯一方式。


12

简短回答:没有(简单)方法,但你可以做一些有助于达成目的的事情。

我曾经做过类似的工具(一个小命令,根据项目描述设置环境、路径、目录等)。我的做法是设置好一切,然后用以下命令生成一个shell:

spawn('bash', ['-i'], {
  cwd: new_cwd,
  env: new_env,
  stdio: 'inherit'
});
执行后,您将进入一个新的shell,其中包含新目录(在我的情况下还包括环境)。当然,您可以更改bash为您喜欢的任何shell。与您最初请求的内容相比,主要区别在于:
  • 增加了一个额外的进程,因此...
  • 您必须编写“exit”才能返回,然后...
  • 退出后,所有更改都将被撤消。

但是,对我而言,这些差异是可取的。


2

尝试这段代码:

const dirPath = path.join(__dirname, foldername);
process.chdir(dirPath);

1
你可以使用 process.chdirprocess.chdir() 方法可更改 Node.js 进程的当前工作目录;如果更改失败(例如,指定的 directory 不存在),则会抛出异常。文档链接:https://nodejs.org/api/process.html#processchdirdirectory
import { chdir, cwd } from 'node:process';

console.log(`Starting directory: ${cwd()}`);
try {
  chdir('/tmp');
  console.log(`New directory: ${cwd()}`);
} catch (err) {
  console.error(`chdir: ${err}`);
}

0
function cd(a, b) {
  var a_path_arry = a.split('/');
  var a_clear_path = [];
  var b_clear_path = [];
  a_path_arry.map((ele) => {
   if(ele != '') a_clear_path.push(ele);
  });

  b_path_arry = b.split('/');
  b_path_arry.map((ele) => {
    if(ele != '') b_clear_path.push(ele);
  });
  b_path_length = b_clear_path.length;

  for(var i=0;i<b_path_length;i++) {
    if(b_clear_path[i] == '.') {
        continue;
    } else if(b_clear_path[i] != '..') {
        a_clear_path.push(b_clear_path[i])
    }  
    else {
        a_clear_path.pop();            
    }
  }

  var path = '';
  a_clear_path.map((ele)=> {
    path += ele + '/';
  });

  console.log(path);

}

cd('a/b/c', './//d/e');

1
虽然这段代码可能回答了问题,但提供有关它如何以及/或为什么解决问题的附加上下文将改善答案的长期价值。您可以在帮助中心找到有关如何编写良好答案的更多信息:https://stackoverflow.com/help/how-to-answer。祝你好运! - nima

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接