Как мне получить путь к текущему скрипту с Node.js?

980

Как бы я получить путь к сценарию в Node.js?

Я знаю, что есть process.cwd, но это относится только к каталогу, где был вызван скрипт, а не к самому скрипту. Например, скажем, я в/home/kyle/ и я запускаю следующую команду:

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

Если я позвоню process.cwd(), я получу /home/kyle/, нет /home/kyle/some/dir/. Есть ли способ получить этот каталог?

Кайл Слэттери
источник
6
nodejs.org/docs/latest/api/globals.html ссылка на документацию принятого ответа.
allenhwkim

Ответы:

1395

Я нашел это после просмотра документации снова. То, что я искал, это переменные уровня __filenameи __dirnameмодуля.

  • __filenameИмя файла текущего модуля. Это разрешенный абсолютный путь к текущему файлу модуля. (например: /home/kyle/some/dir/file.js)
  • __dirnameИмя каталога текущего модуля. (например: /home/kyle/some/dir)
Кайл Слэттери
источник
3
Если вам нужно только имя каталога, а не полный путь, вы можете сделать что-то вроде этого: function getCurrentDirectoryName () {var fullPath = __dirname; var path = fullPath.split ('/'); var cwd = path [path.length-1]; вернуть cwd; }
Энтони Мартин
58
@AnthonyMartin __dirname.split ("/"). Pop ()
19 ч
6
Для тех, кто пробует решение @apx (как я сделал :), это решение не работает в Windows.
Лаужин
36
Или просто__dirname.split(path.sep).pop()
Бурги
47
Илиrequire('path').basename(__dirname);
Вячеслав Котрута
251

В общем, вы можете сделать это:

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

Используйте resol () вместо объединения с '/' или '\', иначе вы столкнетесь с кроссплатформенными проблемами.

Примечание: __dirname - это локальный путь модуля или включенного скрипта. Если вы пишете плагин, который должен знать путь основного скрипта, это:

require.main.filename

или просто получить имя папки:

require('path').dirname(require.main.filename)
Марк
источник
16
Если ваша цель - просто проанализировать файл json и взаимодействовать с ним, вы часто можете сделать это проще с помощью var settings = require('./settings.json'). Конечно, это синхронный fs IO, поэтому не делайте этого во время выполнения, но во время запуска все в порядке, и после загрузки он будет кэширован.
Исааки
2
@ Марк Спасибо! Некоторое время я пытался понять, что __dirname является локальным для каждого модуля. У меня есть вложенная структура в моей библиотеке, и мне нужно знать в нескольких местах корень моего приложения. Рад, что я знаю, как это сделать сейчас: D
Тийс Коерсельман
Узел V8: путь.dirname (process.mainModule.filename)
путь в будущем
Если вы не считаете Windows реальной платформой, можем ли мы пропустить решение? BSD, Macos, Linux, Tizen, Symbian, Solaris, Android, Flutter, Webos все используют / правильно?
Рэй Фосс
Это больше не работает с модулями ES .
Дан Даскалеску
119

Эта команда возвращает текущий каталог:

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());
});
Масуд Сиахкали
источник
Для тех, кто не понимает Асинхронный и Синхронный , смотрите эту ссылку ... stackoverflow.com/a/748235/5287072
DarckBlezzer
16
это как раз то, что OP не хочет ... запрос на путь исполняемого скрипта!
Цезарсоль
3
Текущий каталог - это совсем другое. Если вы запустите что-то вроде cd /foo; node bar/test.js, текущий каталог будет /foo, но сценарий находится в /foo/bar/test.js.
rjmunro
Это не очень хороший ответ. Это запутанная логика, потому что это может быть гораздо более короткий путь, чем вы ожидаете.
kris_IV
Зачем тебе это делать? если файл был относительно текущего каталога, который вы могли бы просто прочитать, text.txtи он работал бы, вам не нужно составлять абсолютный путь
Майкл
105

Используйте __dirname !!

__dirname

Имя каталога текущего модуля. Это так же, как path.dirname ()__filename .

Пример: запуск узла example.js из / Users / mjr

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

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

Для ESModules вы хотели бы использовать: import.meta.url

DDD
источник
1
Это также переживает символические ссылки. Поэтому, если вы создаете корзину и вам нужно найти файл, например, path.join (__dirname, "../example.json"); он все еще будет работать, когда ваш двоичный файл связан в node_modules / .bin
Jason
2
Этот ответ был дан не только несколькими годами ранее, но и с модулями ES .
Дан Даскалеску
48

Когда дело доходит до основного скрипта, это так просто:

process.argv[1]

Из документации Node.js :

process.argv

Массив, содержащий аргументы командной строки. Первым элементом будет «узел», вторым элементом будет путь к файлу JavaScript . Следующими элементами будут любые дополнительные аргументы командной строки.

Если вам нужно знать путь к файлу модуля, используйте __filename .

Лукаш Виктор
источник
3
Может ли downvoter объяснить, почему это не рекомендуется?
Тамлин
1
@Tamlyn Может быть потому, что process.argv[1]применяется только к основному сценарию, а __filenameуказывает на исполняемый файл модуля. Я обновляю свой ответ, чтобы подчеркнуть разницу. Тем не менее, я не вижу ничего плохого в использовании process.argv[1]. Зависит от своих требований.
Лукаш Виктор
10
Если основной скрипт был запущен с помощью диспетчера процессов узла, такого как pm2, process.argv [1] будет указывать на исполняемый файл диспетчера процессов /usr/local/lib/node_modules/pm2/lib/ProcessContainerFork.js
user3002996
34

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));
GOTO 0
источник
Как я узнаю, пишу ли я модуль ES или нет? Это просто вопрос, какую версию Node я использую, или я использую ключевые слова импорта / экспорта?
Эд Браннин
2
Модули ES доступны только с --experimental-modulesфлагом.
Nickensoul
1
--experimental-modules требуется только в том случае, если вы используете версию узла <13.2. просто назовите файл .mjs, а не .js
Brent
28
var settings = 
    JSON.parse(
        require('fs').readFileSync(
            require('path').resolve(
                __dirname, 
                'settings.json'),
            'utf8'));
Foobar
источник
7
Просто примечание: с узла 0.5 вам может потребоваться только файл JSON. Конечно, это не ответит на вопрос.
Кевин Кокс
1
__dirname больше не работает с модулями ES .
Дан Даскалеску
24

Каждая программа Node.js имеет несколько глобальных переменных в своей среде, которые представляют некоторую информацию о вашем процессе, и одна из них __dirname .

Хазарапет Тунанян
источник
Этот ответ был дан не только несколькими годами ранее, __dirname но и с модулями ES .
Дан Даскалеску
Речь идет о NodeJs 10, но этот ответ был опубликован в 2016 году.
Хазарапет Тунанян
Я знаю. Ответы могут быть обновлены по мере изменения технологии.
Дан Даскалеску
13

Я знаю, что это довольно старый вопрос, и исходный вопрос, на который я отвечал, помечен как дубликат и направлен сюда, но я столкнулся с проблемой, пытавшейся заставить журналистов-жасминов работать, и мне не понравилась идея, что мне пришлось понизить рейтинг для того чтобы он работал. Я обнаружил, что 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
 });
Дана Харрис
источник
8

Вы можете использовать process.env.PWD, чтобы получить текущий путь к папке приложения.

AbiSivam
источник
2
ОП запрашивает запрошенный «путь к скрипту». PWD, что означает что-то вроде Process Working Directory, это не так. Кроме того, формулировка "текущего приложения" вводит в заблуждение.
dmcontador
7

Если вы используете pkgдля упаковки своего приложения, вы найдете полезным это выражение:

appDirectory = require('path').dirname(process.pkg ? process.execPath : (require.main ? require.main.filename : process.argv[0]));
  • process.pkgговорит , если приложение было упаковано pkg.

  • process.execPathсодержит полный путь к исполняемому файлу, который /usr/bin/nodeаналогичен прямому вызову scripts ( node test.js) или упакованного приложения.

  • require.main.filename содержит полный путь основного скрипта, но он пуст, когда Node работает в интерактивном режиме.

  • __dirnameсодержит полный путь к текущему сценарию, поэтому я не использую его (хотя это может быть тем, что запрашивает OP; тогда лучше использовать, appDirectory = process.pkg ? require('path').dirname(process.execPath) : (__dirname || require('path').dirname(process.argv[0]));отметив, что в интерактивном режиме __dirnameпусто.

  • В интерактивном режиме используйте либо process.argv[0]для получения пути к исполняемому файлу Node, либо process.cwd()для получения текущего каталога.

dmcontador
источник
3

Используйте basenameметод pathмодуля:

var path = require('path');
var filename = path.basename(__filename);
console.log(filename);

Вот документация, из которой взят пример выше.

Как отметил Дэн, Node работает над модулями ECMAScript с флагом --experimental-modules. Узел 12 все еще поддерживает __dirnameи__filename как указано выше.


Если вы используете --experimental-modulesфлаг, есть альтернативный подход .

Альтернативой является получение пути к текущему модулю ES :

const __filename = new URL(import.meta.url).pathname;

А для каталога, содержащего текущий модуль:

import path from 'path';

const __dirname = path.dirname(new URL(import.meta.url).pathname);
Майкл Коул
источник
-2

Если вы хотите что-то более похожее на $ 0 в сценарии оболочки, попробуйте это:

var path = require('path');

var command = getCurrentScriptPath();

console.log(`Usage: ${command} <foo> <bar>`);

function getCurrentScriptPath () {
    // Relative path from current working directory to the location of this script
    var pathToScript = path.relative(process.cwd(), __filename);

    // Check if current working dir is the same as the script
    if (process.cwd() === __dirname) {
        // E.g. "./foobar.js"
        return '.' + path.sep + pathToScript;
    } else {
        // E.g. "foo/bar/baz.js"
        return pathToScript;
    }
}
dmayo3
источник
__dirnameи не __filenameмогут больше не доступны с модулями ES .
Дан Дакалеску