Jaka jest różnica między __dirname i ./ w node.js?

498

Czy programując w Node.js i odwołując się do plików, które znajdują się gdzieś w stosunku do bieżącego katalogu, czy jest jakiś powód, aby używać tej __dirnamezmiennej zamiast zwykłego ./? Do tej pory używałem ./ w swoim kodzie i właśnie odkryłem istnienie __dirnamei zasadniczo chcę wiedzieć, czy mądrze byłoby przekonwertować moje ./ na to, a jeśli tak, to dlaczego byłby to dobry pomysł .

thisissami
źródło
47
tl; dr: Więc w zasadzie różnica polega na tym, że „./” i „process.cwd ()” odnoszą się do bieżącego katalogu terminala wywołującego skrypt, podczas gdy „__nazwa_katalogu” odnosi się do katalogu, w którym skrypt jest przechowywane.
Gui Imamura
1
Z wyjątkiem sytuacji, gdy .jest używany wewnątrz require. Ścieżka wewnątrz requirejest zawsze względna do pliku zawierającego wywołanie require.
Govind Rai

Odpowiedzi:

814

Istota

W Node.js __dirnamezawsze znajduje się katalog, w którym znajduje się aktualnie wykonywany skrypt ( zobacz to ). Więc jeśli wpisane __dirnamena /d1/d2/myscript.jswartość byłaby /d1/d2.

Z kolei .daje katalog, z którego uruchomiłeś nodepolecenie w oknie terminala (tj. Katalog roboczy), gdy korzystasz z bibliotek takich jak pathi fs. Technicznie zaczyna się jako katalog roboczy, ale można go zmienić za pomocą process.chdir().

Wyjątkiem jest sytuacja, gdy używasz .z require(). Ścieżka wewnątrz requirejest zawsze względna do pliku zawierającego wywołanie require.

Na przykład...

Powiedzmy, że twoja struktura katalogów to

/dir1
  /dir2
    pathtest.js

i pathtest.jszawiera

var path = require("path");
console.log(". = %s", path.resolve("."));
console.log("__dirname = %s", path.resolve(__dirname));

a ty

cd /dir1/dir2
node pathtest.js

dostajesz

. = /dir1/dir2
__dirname = /dir1/dir2

Twój katalog roboczy jest /dir1/dir2więc tym, co .rozwiązuje. Ponieważ pathtest.jsznajduje się w /dir1/dir2tym, to również __dirnamerozwiązuje.

Jeśli jednak uruchomisz skrypt z /dir1

cd /dir1
node dir2/pathtest.js

dostajesz

. = /dir1
__dirname = /dir1/dir2

W takim przypadku Twój katalog roboczy był /dir1taki, że został .rozwiązany, ale __dirnamenadal jest rozwiązany /dir1/dir2.

Używanie .wewnątrz require...

Jeśli w środku dir2/pathtest.jsmasz requirewezwanie do dołączenia pliku dir1, zawsze tak zrobisz

require('../thefile')

ponieważ ścieżka wewnątrz requirejest zawsze względna do pliku, w którym go wywołujesz. Nie ma to nic wspólnego z twoim katalogiem roboczym.

d512
źródło
38
IMO, to wyjaśnienie jest nieco jaśniejsze niż to z zaakceptowanej odpowiedzi (wiesz, „bieżący katalog” jest tam trochę dwuznaczny).
wcielono
5
Zgadzam się. Zmienię przyjętą odpowiedź. Proszę pamiętać, że ta odpowiedź została dodana 2,5 roku po zaakceptowaniu oryginalnej i dopiero teraz ją zauważyłem (kolejne 2 lata później). :) Lepiej późno niż wcale!
thisissami,
14
Warto zauważyć, że ./nie zawsze jest to katalog, z którego uruchomiono węzeł. Zaczyna się w ten sposób, ale można to zmienić za pomocą process.chdir(). Tak więc ./zawsze jest bieżący katalog roboczy, którym zwykle jest węzeł katalogu, z którego został uruchomiony, chyba że kod wyraźnie zmienił katalog roboczy.
gilly3
3
Jestem trochę zdezorientowany co do używania. wewnątrz wymaga części, jeśli ścieżka wewnątrz wymaga zawsze odnosi się do pliku, który wywołujesz, czy ścieżka nie powinna być wymagana („../ plik”) zamiast wymagania („../ katalog1 / plik”)? myślałem, że .. przyniosę bieżącą pozycję ścieżki z powrotem o jeden poziom z dir2 do dir1 już. Czy nadal musisz umieścić ścieżkę dir1 na ścieżce, czy też coś mi umknęło?
andromada,
1
A jak byś zrobił, gdybyś musiał użyć ../someDirjakiegoś skryptu i zamierzasz uruchomić polecenie z innego folderu?
cbdeveloper
154

./odnosi się do bieżącego katalogu roboczego, z wyjątkiem require()funkcji. Podczas używania require()tłumaczy ./się do katalogu bieżącego pliku o nazwie. __dirnamejest zawsze katalogiem bieżącego pliku.

Na przykład o następującej strukturze plików

/home/user/dir/files/config.json

{
  "hello": "world"
}

/home/user/dir/files/somefile.txt

text file

/home/user/dir/dir.js

var fs = require('fs');

console.log(require('./files/config.json'));
console.log(fs.readFileSync('./files/somefile.txt', 'utf8'));

Jeśli cdwejdę /home/user/diri ucieknę node dir.js, dostanę

{ hello: 'world' }
text file

Ale kiedy uruchamiam ten sam skrypt /home/user/, otrzymuję

{ hello: 'world' }

Error: ENOENT, no such file or directory './files/somefile.txt'
    at Object.openSync (fs.js:228:18)
    at Object.readFileSync (fs.js:119:15)
    at Object.<anonymous> (/home/user/dir/dir.js:4:16)
    at Module._compile (module.js:432:26)
    at Object..js (module.js:450:10)
    at Module.load (module.js:351:31)
    at Function._load (module.js:310:12)
    at Array.0 (module.js:470:10)
    at EventEmitter._tickCallback (node.js:192:40)

Używanie ./działało z, requireale nie dla fs.readFileSync. To dlatego, że dla fs.readFileSync, ./przekłada się na CWD (w tym przypadku /home/user/). I /home/user/files/somefile.txtnie istnieje.

fent
źródło
och, myślałem, że __nazwa_katalogu to bieżący katalog roboczy ... dzięki za wyjaśnienie!
thisissami,
Czy istnieje jakiś sposób odwoływania się do katalogu roboczego aplikacji za pomocą fs? Na przykład próbuję załadować plik z katalogu roboczego /movies, ale ponieważ mój moduł znajduje się w pliku /custom_modules/, __dirnamepróbuje pobrać film z/custom_modules/movies
2
Możesz użyć ./lub process.cwd(). patrz nodejs.org/api/process.html#process_process_cwd
fent
Warto zauważyć, że nie jest dobrym pomysłem używanie __dirname zamiast ./ w instrukcjach wymaga, ponieważ chociaż zachowują się identycznie w węźle, mogą powodować problemy z kompilacjami Browserify dla pakietów, których w inny sposób można łatwo uniknąć.
Jed Watson,