如何使用Node.js获取当前脚本的路径?

1307

如何在Node.js中获取脚本路径?

我知道有process.cwd,但这只是指调用脚本的目录,而不是脚本本身的目录。例如,假设我在/home/kyle/中,并运行以下命令:

node /home/kyle/some/dir/file.js

如果我调用process.cwd(),我得到的是/home/kyle/而不是/home/kyle/some/dir/。有没有一种方法可以获取那个目录?


6
这是被接受的答案的文档链接,其中包含有关全局变量的详细信息。请注意,这是英文文档。 - allenhwkim
这对我很有帮助:const sqlFolder = path.join(path.join(process.cwd(), 'db'), 'queries'); https://vercel.com/guides/loading-static-file-nextjs-api-route - Ryan
15个回答

1724

我再次查看文档后找到了它。我需要查找的是模块级别的变量__filename__dirname

  • __filename是当前模块的文件名。这是当前模块文件的解析绝对路径。(例如:/home/kyle/some/dir/file.js)
  • __dirname是当前模块的目录名。(例如:/home/kyle/some/dir)

70
当前目录的最后一个文件夹名称。 - 19h
7
尝试使用 @apx 提供的解决方案的人(就像我一样),需要注意这个解决方案在Windows系统上无效。 - Laoujin
49
这段代码的作用是获取当前文件所在的目录名。它先使用__dirname获取当前文件所在的完整路径,然后使用split方法按照操作系统的分隔符将路径分割成一个数组,最后使用pop方法获取数组中最后一个元素,也就是当前文件所在的目录名。 - Burgi
64
require('path').basename(__dirname); 的意思是获取当前文件所在文件夹的名称。 - Vyacheslav Cotruta
13
这段代码不再适用于ES模块 - Dan Dascalescu
显示剩余6条评论

316

所以基本上你可以这样做:

fs.readFile(path.resolve(__dirname, 'settings.json'), 'UTF-8', callback);

使用resolve()代替使用'/'或'\'进行字符串拼接,否则会遇到跨平台问题。

注意:__dirname是模块或包含脚本的本地路径。如果你正在编写需要知道主脚本路径的插件,则应该使用:

require.main.filename

或者,仅获取文件夹名称:

require('path').dirname(require.main.filename)

17
如果您的目标仅是解析并与json文件交互,您通常可以通过var settings = require('./settings.json')更轻松地实现此目的。当然,这是同步的fs IO操作,因此不要在运行时进行操作,但在启动时进行操作是可以的,一旦加载完成,它就会被缓存。 - isaacs
3
谢谢Marc!一段时间以来,我一直在试图解决__dirname只是每个模块本地路径的问题。我的库有嵌套结构,并且需要在几个地方知道应用程序的根目录。很高兴现在知道如何做到这一点:D - Thijs Koerselman
Node V8: path.dirname(process.mainModule.filename) Node V8:path.dirname(process.mainModule.filename) - wayofthefuture
3
这个不再适用于ES模块 - Dan Dascalescu
@forrestthopkinsa 基本上我不支持任何允许在文件名中使用正斜杠的文件系统。Windows 存在,但是 WSL、Docker 和 BusyBox 也存在... 我想不出任何理由去考虑 DOS 的怪癖,除非是为了编写 POSIX 层、迁移遗留代码库或制作文件系统存储适配器;即使这样,云也是一切,而且云中 99.9% 是 Linux。 - Ray Foss
显示剩余3条评论

178

Node.js 10支持ECMAScript模块,其中__dirname__filename不再可用。

因此,要获取当前ES模块的路径,必须使用:

import { fileURLToPath } from 'url';

const __filename = fileURLToPath(import.meta.url);

对于包含当前模块的目录:

import { dirname } from 'path';
import { fileURLToPath } from 'url';

const __dirname = dirname(fileURLToPath(import.meta.url));

3
ES模块只能在使用--experimental-modules标志的情况下使用。 - Nickensoul
5
只有当你运行的 Node 版本低于 13.2 时才需要使用 --experimental-modules。只需将文件名从 .js 改为 .mjs 即可。 - Brent
谢谢,这个解决了我的问题!对我来说,它似乎非常适合向后兼容性支持。 - Gal Grünfeld
2
是的,这个可以运行。@Nickensoul 或者在 package.json 中写成 "type": "module"。 - Johan Hoeksma
警告,如果您正在使用打包工具,这是源文件的路径。如果您构建并部署到不同的文件夹,它仍然是原始源文件的路径。 - Dirigible
显示剩余3条评论

171

使用 __dirname!!

__dirname

当前模块所在的目录名称。与__filenamepath.dirname()相同。

例如:在路径 /Users/mjr 下运行 node example.js。

console.log(__dirname);
// Prints: /Users/mjr
console.log(path.dirname(__filename));
// Prints: /Users/mjr

https://nodejs.org/api/modules.html#modules_dirname

对于ES模块,您需要使用:import.meta.url


1
这也可以通过符号链接进行存活。因此,如果您创建了一个bin并需要找到一个文件,例如 path.join(__dirname, "../example.json");当您的二进制文件链接在node_modules/.bin中时,它仍然有效。 - Jason
6
不仅这个答案是几年前给出的,而且它也不再适用于ES模块 - Dan Dascalescu

155

这个命令会返回当前目录:

var currentPath = process.cwd();
例如,使用路径来读取文件:
var fs = require('fs');
fs.readFile(process.cwd() + "\\text.txt", function(err, data)
{
    if(err)
        console.log(err)
    else
        console.log(data.toString());
});

对于那些不理解“异步”和“同步”的人,请查看此链接... https://dev59.com/6XRB5IYBdhLWcg3wAjTR#748235 - DarckBlezzer
33
这正是原帖作者不想要的...请求的是可执行脚本的路径! - caesarsol
10
当前目录是非常不同的东西。如果您执行类似于cd /foo; node bar/test.js这样的命令,当前目录将是/foo,但脚本位于/foo/bar/test.js - rjmunro
2
这不是一个好的答案。这种逻辑混乱,因为这条路可以比你预期的要短得多。 - kris_IV
2
为什么你要这样做呢?如果文件相对于当前目录,你只需要读取 text.txt 就可以了,不需要构造绝对路径。 - Michael Mrozek

56

谈到主脚本,它就像这样简单:

process.argv[1]

根据 Node.js 文档

process.argv

包含命令行参数的数组。第一个元素是 'node',第二个元素是JavaScript 文件的路径。接下来的元素是其他任何命令行参数。

如果您需要知道模块文件的路径,请使用 __filename


4
请问给出负评的人能否解释一下为什么这不被推荐? - Tamlyn
3
也许是因为 process.argv[1] 只适用于主脚本,而 __filename 指向正在执行的模块文件。我更新了我的答案来强调这种区别。不过,我认为使用 process.argv[1] 并没有什么问题。这取决于个人需求。 - Lukasz Wiktor
15
如果主程序是通过像pm2这样的节点进程管理器启动的,则process.argv[1]将指向进程管理器的可执行文件。/usr/local/lib/node_modules/pm2/lib/ProcessContainerFork.js - user3002996
1
@LukaszWiktor 非常感谢!与自定义的Node.js CLI完美配合 :-) - bgrand-ch

27
var settings = 
    JSON.parse(
        require('fs').readFileSync(
            require('path').resolve(
                __dirname, 
                'settings.json'),
            'utf8'));

7
从Node 0.5版本开始,你可以直接使用require来引用JSON文件。当然这并没有回答问题。 - Kevin Cox
3
__dirname 在 ES 模块中已不再可用。详情请见此处 - Dan Dascalescu

21

每个 Node.js 程序都有一些全局变量在其环境中,它们代表有关您的进程的某些信息,其中之一是 __dirname


2
这个答案不仅是几年前给出的,而且 __dirname 不再适用于ES模块 - Dan Dascalescu
这是关于NodeJs 10的内容,但是这个答案是在2016年发布的。 - Hazarapet Tunanyan

17

我知道这已经很旧了,我要回复的原始问题被标记为重复并被引导到这里,但我遇到了一个问题,尝试让jasmine-reporters工作,但不想只能通过降级来解决它。我发现jasmine-reporters没有正确解析savePath,实际上是将报告文件夹输出放在了jasmine-reporters目录中,而不是在我运行gulp的根目录中。为了使其正常工作,我最终使用了process.env.INIT_CWD来获取初始工作目录,这应该是你运行gulp的目录。希望这能帮助有需要的人。

var reporters = require('jasmine-reporters');
var junitReporter = new reporters.JUnitXmlReporter({
  savePath: process.env.INIT_CWD + '/report/e2e/',
  consolidateAll: true,
  captureStdout: true
 });

15
使用 path 模块的 basename 方法:
var path = require('path');
var filename = path.basename(__filename);
console.log(filename);

这里是上面例子的文档。

正如丹指出的那样,Node正在使用“--experimental-modules”标志开发ECMAScript模块。Node 12仍然支持上述的__dirname__filename


如果您正在使用--experimental-modules标志,有一种替代方法
替代方法是获得当前ES模块的路径
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(new URL(import.meta.url));

对于包含当前模块的目录:

import { fileURLToPath } from 'url';
import path from 'path';

const __dirname = path.dirname(fileURLToPath(new URL(import.meta.url)));

在Windows中,使用此方法返回的路径前面有一个斜杠(/)。 - Aalex Gabi

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