關於Node.js的__dirname,__filename,process.cwd(),./文件路徑的一些坑

原由

原文收錄在個人 GitHub博客 (https://github.com/jawil/blog) ,喜歡的能夠關注最新動態,你們一塊兒多交流學習,共同進步,以學習者的身份寫博客,記錄點滴。html

最近在學習Node.js裏面的fs模塊,遇到了一個比較詭異的現象,踩到了坑,就是讀取當前目錄下的一個文件,死活讀取不到,因爲以前對於Node.js裏面的path模塊也不太熟悉,也沒系統研究過,因此今天就踩了這個坑,記錄踩坑的過程,防止之後踩坑和你們也踩坑。node

說一下當時的情形:
QQ20170510-181437git

我納悶的很半天,我明明就是讀取當前目錄下的1.findLargest.js,爲何提示找不到這個文件,運行了幾遍,死活找不到1.findLargest.js這個文件。github

後來才發現是由於運行這個文件不是從當前目錄運行了,從圖中能夠看出,當前的目錄是/Users/jawil/Desktop/nodejs/demo/ES6-lottery/syntax/nodejs,而我運行這個腳本的目錄是/Users/jawil/Desktop/nodejs/demo/ES6-lottery;這就是問題的所在了。不過爲何運行腳本的位置也會影響這個路徑呢,且往下看。學習

探索

計算機不會欺騙人,一切按照規則執行,說找不到這個文件,那確定就是真的找不到,至於爲何找不到,那就是由於咱們理解有誤差,我最初理解的'./'是當前執行js文件所在的文件夾的絕對路徑,而後Node.js的理解卻不是這樣的,咱們慢慢往下看。ui

Node.js中的文件路徑大概有 __dirname__filenameprocess.cwd()./ 或者 ../,前三個都是絕對路徑,爲了便於比較,./ 和 ../ 咱們經過 path.resolve('./')來轉換爲絕對路徑。spa

簡單說一下這幾個路徑的意思,:翻譯

__dirname:    得到當前執行文件所在目錄的完整目錄名
__filename:   得到當前執行文件的帶有完整絕對路徑的文件名
process.cwd():得到當前執行node命令時候的文件夾目錄名 
./:           文件所在目錄

先看一看我電腦當前的目錄結構:code

syntax/
    -nodejs/
        -1.findLargest.js
        -2.path.js
        -3.fs.js
    -regs
        -regx.js
        -test.txt

在 path.js 裏面咱們寫這些代碼,看看輸出是什麼:htm

const path = require('path')
console.log('__dirname:', __dirname)
console.log('__filename:', __filename)
console.log('process.cwd():', process.cwd())
console.log('./:', path.resolve('./'))

在當前目錄下也就是nodejs目錄運行 node path.js,咱們看看輸出結果:

__dirname:     /Users/jawil/Desktop/nodejs/demo/ES6-lottery/syntax/nodejs
__filename:    /Users/jawil/Desktop/nodejs/demo/ES6-lottery/syntax/nodejs/2.path.js
process.cwd(): /Users/jawil/Desktop/nodejs/demo/ES6-lottery/syntax/nodejs
./:            /Users/jawil/Desktop/nodejs/demo/ES6-lottery/syntax/nodejs

QQ20170510-183712

而後在 項目根目錄ES6-lottery 目錄下運行 node syntax/nodejs/2.path.js,咱們再來看看輸出結果:

__dirname:     /Users/jawil/Desktop/nodejs/demo/ES6-lottery/syntax/nodejs
__filename:    /Users/jawil/Desktop/nodejs/demo/ES6-lottery/syntax/nodejs/2.path.js
process.cwd(): /Users/jawil/Desktop/nodejs/demo/ES6-lottery
./:            /Users/jawil/Desktop/nodejs/demo/ES6-lottery

QQ20170510-184943

答案顯而易見?咱們能夠經過上面的例子對比,暫時得出表面的結論:

  • __dirname: 老是返回被執行的 js 所在文件夾的絕對路徑

  • __filename: 老是返回被執行的 js 的絕對路徑

  • process.cwd(): 老是返回運行 node 命令時所在的文件夾的絕對路徑

  • ./: 跟 process.cwd() 同樣,返回 node 命令時所在的文件夾的絕對路徑

可是,咱們再來看看這個例子,咱們在上面的例子加幾句代碼,而後:

咱們在1.findLargest.js先加這句代碼

exports.A = 1;

再來在剛纔報錯的3.fs.js裏面加這兩句代碼看看:

const test = require('./1.findLargest.js');

console.log(test)

運行node syntax/nodejs/3.fs.js,最後看看結果:

QQ20170510-185549

再次疑惑

爲何都是讀取./1.findLargest.js文件,同樣的路徑,爲何require能獲取到,而readFile讀取不到呢?

因而查了很多資料,看到了一些關於require引入模塊的機制,從中學到了很多,也明白了爲何是這樣。

咱們先了解一下require() 的基本用法:

下面的內容來自require() 源碼解讀,由阮一峯翻譯自《Node使用手冊》。

QQ20170510-190528

咱們從第(2)小條的a部分能夠看出:

(2)若是 X 以 "./" 或者 "/" 或者 "../" 開頭 
  a. 根據 X 所在的父模塊,肯定 X 的絕對路徑。
  b. 將 X 當成文件,依次查找下面文件,只要其中有一個存在,就返回該文件,再也不繼續執行。

const test = require('./1.findLargest.js')按照上面規則翻譯一遍就是:

  1. 根據1.findLargest.js所在的父模塊,肯定1.findLargest.js的絕對路徑爲/Users/jawil/Desktop/nodejs/demo/ES6-lottery/syntax/nodejs,關於其中的尋找細節這裏不作探討。

  2. 先把1.findLargest.js當成文件,依次查找當前目錄下的1.findLargest.js,找到了,就返回該文件,再也不繼續執行。

根據require的基本規則,對於上面出現的情形也就不足爲奇了,更多require的機制和源碼解讀,請移步:
require() 源碼解讀

那麼關於 ./ 正確的結論是:
在 require() 中使用是跟 __dirname 的效果相同,不會由於啓動腳本的目錄不同而改變,在其餘狀況下跟 process.cwd() 效果相同,是相對於啓動腳本所在目錄的路徑。

總結:

  • __dirname: 得到當前執行文件所在目錄的完整目錄名

  • __filename: 得到當前執行文件的帶有完整絕對路徑的文件名

  • process.cwd():得到當前執行node命令時候的文件夾目錄名

  • ./: 不使用require時候,./與process.cwd()同樣,使用require時候,與__dirname同樣

只有在 require() 時才使用相對路徑(./, ../) 的寫法,其餘地方一概使用絕對路徑,以下:

// 當前目錄下
 path.dirname(__filename) + '/path.js'; 
// 相鄰目錄下
 path.resolve(__dirname, '../regx/regx.js');

最後看看改過以後的結果,不會報錯找不到文件了,無論在哪裏執行這個腳本文件,都不會出錯了,防止之後踩坑。

QQ20170510-193604

相關文章
相關標籤/搜索