ตรวจสอบว่าเรียกผ่านต้องหรือโดยตรงโดยบรรทัดคำสั่ง


298

ฉันจะตรวจสอบว่าไฟล์ Node.js ของฉันถูกเรียกใช้ SH: node path-to-fileหรือ JS: require('path-to-file')?

นี่คือ Node.JS เทียบเท่ากับคำถามก่อนหน้าของฉันใน Perl: ฉันจะรันสคริปต์ Perl ของฉันได้อย่างไรหากไม่ได้โหลดมาพร้อมกับจำเป็นต้องใช้?


คำตอบ:


471
if (require.main === module) {
    console.log('called directly');
} else {
    console.log('required as a module');
}

ดูเอกสารสำหรับที่นี่: https://nodejs.org/docs/latest/api/modules.html#modules_accessing_the_main_module


3
มีวิธีใดบ้างที่จะหลีกเลี่ยงปัญหานี้ ฉันมีรหัส (ซึ่งฉันไม่สามารถควบคุมได้) ที่ทำสิ่งนี้ แต่ฉันจำเป็นต้องมี () มันและให้มันทำหน้าที่ราวกับว่ามันถูกเรียกโดยตรง โดยพื้นฐานแล้วฉันต้องหลอกสิ่งที่ใช้การทดสอบนั้นเพื่อคิดว่ามันถูกเรียกโดยตรง
เควิน

2
@ เควินฉันไม่รู้เกี่ยวกับการทำเช่นนี้require()แต่คุณสามารถทำได้ด้วยการนำเข้าไฟล์จากนั้นก็รันevalบนมันหรือโดยการเรียกใช้require('child_process').exec('node the_file.js')
MalcolmOcean

เมื่อใช้โมดูล ES กับ Node.js คุณสามารถใช้es-mainแพ็คเกจเพื่อตรวจสอบว่ามีการเรียกใช้โมดูลโดยตรง
Tim Schaub

91

มีอีกวิธีที่สั้นกว่าเล็กน้อย (ไม่ได้ระบุไว้ในเอกสารที่กล่าวถึง)

var runningAsScript = !module.parent;

ฉันระบุรายละเอียดเพิ่มเติมเกี่ยวกับวิธีการทำงานทั้งหมดนี้ภายใต้ประทุนในโพสต์บล็อกนี้


+1 ฉันชอบสิ่งนี้ดีกว่า แต่ฉันจะลังเลก่อนที่จะเปลี่ยนคำตอบที่ยอมรับ :)
Bryan Field

8
ดังที่ฉันได้ระบุวิธีที่เป็นทางการที่บันทึกไว้คือ @nicolaskruchten ที่ระบุไว้ นี่เป็นอีกทางเลือกหนึ่งไม่จำเป็นต้องเปลี่ยนคำตอบที่ยอมรับได้ ทั้งงาน
Thorsten Lorenz

10
ฉันต้องใช้สิ่งนี้แทนที่จะใช้วิธีที่เป็นเอกสาร - วิธีที่เป็นเอกสารใช้สำหรับเช่น แต่ไม่node script.js cat script.js | nodeวิธีนี้ใช้ได้ผลกับทั้งคู่
ทิมมาโลน

9

ฉันสับสนเล็กน้อยโดยคำศัพท์ที่ใช้ในคำอธิบาย ดังนั้นฉันจึงต้องทำแบบทดสอบสองสามฉบับ

ฉันพบว่าสิ่งเหล่านี้ให้ผลลัพธ์ที่เหมือนกัน:

var isCLI = !module.parent;
var isCLI = require.main === module;

และสำหรับคนที่สับสนอื่น ๆ (และเพื่อตอบคำถามโดยตรง):

var isCLI = require.main === module;
var wasRequired = !isCLI;

5

เช่นเดียวกับใน Python ฉันมักจะพบว่าตัวเองพยายามจดจำวิธีการเขียนข้อมูลโค้ดนี้ ดังนั้นฉันตัดสินใจที่จะสร้างโมดูลง่าย ๆ สำหรับมัน ฉันใช้เวลาเล็กน้อยในการพัฒนาเนื่องจากการเข้าถึงข้อมูลโมดูลของผู้โทรไม่ได้ตรงไปตรงมา แต่มันสนุกที่ได้เห็นว่ามันสามารถทำได้

ดังนั้นความคิดคือการเรียกโมดูลและถามว่าโมดูลผู้โทรเป็นหลักหรือไม่ เราต้องหาโมดุลของฟังก์ชั่นผู้โทร วิธีแรกของฉันคือรูปแบบของคำตอบที่ยอมรับได้:

module.exports = function () {
    return require.main === module.parent;
};

แต่นั่นไม่รับประกันว่าจะทำงาน module.parentชี้ไปที่โมดูลซึ่งโหลดเราลงในหน่วยความจำไม่ใช่ตัวเรียกเรา หากเป็นโมดูลผู้โทรที่โหลดโมดูลตัวช่วยนี้ลงในหน่วยความจำก็ถือว่าใช้ได้ แต่ถ้าไม่ใช่เราก็ไร้ประโยชน์ ดังนั้นเราต้องลองอย่างอื่น โซลูชันของฉันคือสร้างการติดตามสแต็กและรับชื่อโมดูลของผู้โทรจากที่นั่น:

module.exports = function () {
    // generate a stack trace
    const stack = (new Error()).stack;
    // the third line refers to our caller
    const stackLine = stack.split("\n")[2];
    // extract the module name from that line
    const callerModuleName = /\((.*):\d+:\d+\)$/.exec(stackLine)[1];

    return require.main.filename === callerModuleName;
};

ตอนนี้เราสามารถทำได้:

if (require("./is-main-module")()) {  // notice the `()` at the end
    // do something
} else {
    // do something else
}

หรืออ่านได้มากขึ้น:

const isMainModule = require("./is-main-module");

if (isMainModule()) {
    // do something
} else {
    // do something else
}

เป็นไปไม่ได้ที่จะลืม :-)


2
เด็ดมาก ฉันชอบมันเมื่อตัวอย่างโค้ดทั่วไปย่อมาจากชื่อเดียว การปรับเล็กน้อย:return require.main /*this is undefined if we started node interactively*/ && require.main.filename === callerModuleName;
masterxilo

4

ลองใช้วิธีนี้หากคุณใช้โมดูล ES6:

if (process.mainModule.filename === __filename) {
  console.log('running as main module')
}

2
อึ, ฉันprocess.mainModuleคือundefined
datdinhquoc

1
GHOSHHHH ฉันต้องตรวจสอบเรื่องนี้ในไฟล์. mjs ของฉัน
datdinhquoc
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.