source

require를 통해 호출되는지 아니면 명령줄을 통해 직접 호출되는지 탐지

manysource 2023. 5. 15. 22:22

require를 통해 호출되는지 아니면 명령줄을 통해 직접 호출되는지 탐지

내 노드의 여부를 탐지하려면 어떻게 해야 합니까?SH를 사용하여 JS 파일을 호출했습니다.node path-to-file또는 JS:require('path-to-file')?

노드입니다.이전 Perl 질문과 동일한 JS: require가 로드되지 않은 경우에만 Perl 스크립트를 실행하려면 어떻게 해야 합니까?

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 에서 참조하십시오.

조금 더 짧은 또 다른 방법이 있습니다(위에서 언급한 문서에는 설명되어 있지 않음).

var runningAsScript = !module.parent;

저는 이 블로그 게시물에서 이 모든 것이 어떻게 작동하는지에 대해 더 자세히 설명했습니다.

ES Module(및 Node 10.12+)을 사용하는 경우 다음을 사용할 수 있습니다.import.meta.url:

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

const nodePath = path.resolve(process.argv[1]);
const modulePath = path.resolve(fileURLToPath(import.meta.url))
const isRunningDirectlyViaCLI = nodePath === modulePath

같은 것들require.main,module.parent그리고.__dirname/__filename ESM에서는 사용할 수 없습니다.

참고: ESLint를 사용하는 경우 이 구문이 질식할 수 있습니다. 이 경우 ESLint로 업데이트하고 다음을 수행해야 합니다.ecmaVersion까지11(2020).

추가 정보: ,

저는 설명에 사용된 용어 때문에 약간 혼란스러웠습니다.그래서 저는 몇 가지 간단한 테스트를 해야 했습니다.

다음과 같은 결과가 나온다는 것을 알게 되었습니다.

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

그리고 다른 혼란스러운 사람들을 위해 (그리고 질문에 직접 대답하기 위해):

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

ES6 모듈을 사용하는 경우 다음을 시도합니다.

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

저는 항상 이 빌어먹을 코드 조각을 쓰는 방법을 기억하려고 노력하는 제 자신을 발견합니다. 그래서 저는 그것을 위한 간단한 모듈을 만들기로 결정했습니다.호출자의 모듈 정보에 접근하는 것이 간단하지 않기 때문에 작동하는 데 시간이 좀 걸렸지만, 어떻게 할 수 있는지 보는 것은 재미있었습니다.

그래서 아이디어는 모듈을 호출하여 발신자 모듈이 메인 모듈인지 묻는 것입니다.우리는 발신자 기능의 모듈을 파악해야 합니다.저의 첫 번째 접근 방식은 수용된 답변의 변형이었습니다.

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;
};

다음으로 저장is-main-module.js이제 다음을 수행할 수 있습니다.

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

if (isMainModule()) {
    console.info("called directly");
} else {
    console.info("required as a module");
}

어느 쪽이 기억하기가 더 쉽습니까?

먼저, 문제를 더 잘 정의해 보겠습니다.내 생각에 당신이 정말로 찾고 있는 것은 당신의 스크립트가 소유하고 있는지 여부입니다. process.argv (즉, 스트가담지는여부하당처를리립크process.argv 이러한 가정을 바탕으로 아래의 코드와 테스트는 정확합니다.

module.parent로 더 하지 않습니다. (이 경우 를 가질 수 .)module.parent부모만 ). 다음과 같은 경우를 합니다. 따라서 다음과 같은 미래 보장 조건을 사용하여 모든 경우를 처리합니다.

if (
  typeof process === 'object' && process && process.argv
   && (
    (
      typeof module === 'object' && module
       && (
        !module.parent
         || require.main === module
         || (process.mainModule && process.mainModule.filename === __filename)
         || (__filename === "[stdin]" && __dirname === ".")
       )
    )
    || (
      typeof document === "object"
      && (function() {
       var scripts = document.getElementsByTagName("script");
       try { // in case we are in a special environment without path
         var normalize = require("path").normalize;
         for (var i=0,len=scripts.length|0; i < len; i=i+1|0)
           if (normalize(scripts[i].src.replace(/^file:/i,"")) === __filename)
             return true;
       } catch(e) {}
      })()
    )
   )
) {
    // this module is top-level and invoked directly by the CLI
    console.log("Invoked from CLI");
} else {
    console.log("Not invoked from CLI");
}

다음과 같은 경우 모든 스크립트에서 올바르게 작동하며 오류가 발생하지 않습니다.

  • 가 필요한 x).require('./main.js'))
  • 직접 x).nodejs cli.js)
  • 다른 스크립트(예: x)를 미리 로드하는 중입니다.nodejs -r main.js cli.js)
  • CLI "CLI"("x"))에 합니다.cat cli.js | nodejs)
  • 배관( 예이있예배관는(압: x)).cat cli.js | nodejs -r main.js)
  • 경우(new Worker('./worker.js'))
  • eval교육노예자동예(▁(자동:노▁edenew Worker('if (<test for CLI>) ...', {eval: true}))
  • 모듈 내부( ES6 모듈("x"))nodejs --experimental-modules cli-es6.js)
  • "x")가 있는 모듈.nodejs --experimental-modules -r main-es6.js cli-es6.js)
  • 모듈 파이형 ES6예듈(모: x))cat cli-es6.js | nodejs --experimental-modules)
  • 예: x)cat cli-es6.js | nodejs --experimental-modules -r main-es6.js)
  • 경우 입니다.)process.argv)
  • 브라우저 환경:을 통해 하는 경우: ElectronJS)에서 인라인 스크립트와 모든 모듈이 로드되는 경우<script>CLI로 됩니다).

하지 않는 이작지않예유는경최스일한우트: x)를 미리 입니다.nodejs -r cli.js cli.js이할 수 . . . . . . . . . . . . . . . . . . . . 예cat cli.js | nodejs -r cli.js이는 스크립트를 두 번(필요한 모듈로 한 번, 최상위 수준으로 한 번) 실행하기 때문입니다.미리 로드된 스크립트 내부에서 기본 스크립트가 무엇인지 알 수 없기 때문에 이 문제에 대한 가능한 해결책은 없다고 생각합니다.

이론적으로, 오류는 물체에 대한 게터 내부에서 발생할 수 있습니다(예: 누군가가 할 만큼 미친 경우).Object.defineProperty(globalThis, "process", { get(){throw 0} });그러나 이는 모든 환경에서 코드 스니펫에 사용되는 속성의 기본 상황에서는 절대로 발생하지 않습니다.

유닉스 호출되었는지 모듈 가져오기("node.js" ("node.js" ("node.js" ("node"))를 를 탐지하는 입니까?import {foo} from 'bar.js')

이러한 기능은 노출되지 않습니다.현재로서는 CLI와 라이브러리 논리를 별도의 파일로 분리해야 합니다.

node.js 핵심 기여자인 devsnick의 응답이 nodejs/help/issions/2420에 응답합니다.

제 관점에서 옳은 답입니다.

언급URL : https://stackoverflow.com/questions/6398196/detect-if-called-through-require-or-directly-by-command-line