V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
xcold
V2EX  ›  程序员

这事要从 node node.js 说起

  •  
  •   xcold ·
    x-cold · 2017-08-27 20:42:30 +08:00 · 4042 次点击
    这是一个创建于 2645 天前的主题,其中的信息可能已经有所发展或是发生改变。

    导读:兴许所有程序员都有命名困难症,在考虑变量、常量、方法、类、文件等命名时,总会千方百计尝试一些语义化的方式去实现。

    曾经有那么一段时间,一些 node 初学的同学遇到了同样的问题:Hello World 跑不动!

    原文首发于个人博客:这事要从 node node.js 说起

    1. 谜之 Hello World

    问题的起源非常简单,当我们在编写一个入门程序时,就会迅速想起那句脍炙人口的语句:

    console.log('Hello World');
    

    于是乎,顺手保存为 node.js ,紧接着尝试以node node.js来运行该示例程序。毫无疑问,在 cmd 环境下,会遇到如下的报错:

    exec

    ( PS:实际上无论是 Mac、Linux 用户,亦或是 WIndows 中使用 Powershell 或其他终端环境的同学都无法与此问题完美邂逅)

    3. 初步分析

    此时此刻,心中一阵失落,居然连入门的示例程序都无法运行,不禁一阵瞎想:是否该放弃 node.js 了?

    言归正传,细心的同学会发现,报错的源头来自Windows Script Host,下简称WSH,我们不难查到它是 Windows 操作系统脚本语言程序( script,即:脚本)的运行环境。

    3. 执行了什么?

    简单分析一下node node.js这条命令,我们会很自然地认定为:执行 node.exe 程序,参数为 node.js 。

    然而实际上,真正执行的程序却变成WSH,前面执行的命令node node.js并没有任何跟调起WSH相关的逻辑,因此为何调起了WSH成为了解谜的关键。

    顺蔓摸瓜,由于WSH正好是执行脚本的服务,而 js 恰恰又是脚本的一种,不妨假设node.js这个脚本文件就是罪魁祸首。然后创建一个test.js的副本,尝试执行它:

    exec2

    2.1 执行程序的路径

    根据试验的结果不难猜出node node.js命令实际执行了node.js这个脚本文件,从而调起WSH服务,进而出现上图的报错。

    顺水推舟可确定node node.js等价于.\node.js node.js,即命令执行的文件完整的路径为:E:\test\node.js

    ( PS:各位看官切莫介怀''作为路径分隔符,毕竟在 cmd 下'/'担任参数分隔符的要职)

    2.2 补全程序的路径

    先讲讲通用的说法,无论是 * nix、OS/2、DOS 亦或是 windows,其 terminal 都可以通过一个特殊的环境变量PATH进行“补全”(关于环境变量的详细内容本文不作介绍)。

    接下来我们通过 ping 命令先做简要说明:

    2.2.1 定位程序的路径

    ping

    很明显,在任何一台正常的机器上,这条命令执行后都能得到期待的结果。此时我们可以看到该 cmd 进程下的PATH环境变量中包含C:\WINDOWS\system32,通过对PATH中的元素(文件夹路径)即可将 ping 程序的路径补全为:C:\WINDOWS\system32\ping。(在 * nix 系统下依然通用)

    path

    2.2.2 补全后缀名(仅 windows、dos)

    由于 windows 的可执行的概念和 * nix 略有不同,因此在 windows 平台下还需要对程序进行后缀名的补全。

    其中在 * inx 下,只需保证文件的结构符合规范,并且拥有可执行权限,就可以执行;而在 windows 下,还需要考虑其后缀名及执行方式(实际上是一种打开方式的策略)。

    E:\test>echo %PATHEXT%
    .COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC;.PY;.PYW;.CPL
    

    最终我们补全的程序路径为:C:\WINDOWS\system32\ping.exe

    2.2.3 特别注意(仅 windows、dos)

    针对于 cmd 环境,当前目录也会作为路径补全的一部分,并且优先级最高。在当前目录下,我们创建一个ping.bat的脚本,并填充以下内容:

    @echo off
    :: 输出完整的路径和文件名及后缀
    echo %~dpnx0
    

    执行结果如下图,原来的ping.exe的动作明显被覆盖了。

    current_path

    2.2.4 小结

    我们也额外地发现 windows 的默认可执行的后缀名包含.JS,由此可推断最初的那条node node.js命令最终补全的程序路径为:E:\test\node.js

    3 打开方式?

    从 2.2.4 的结论中能显而易见的推导出命令执行的程序为node.js脚本文件,那么它为什么是通过WSH去执行的呢?

    答案其实很明显,有个通俗易懂的概念,叫做打开方式,而 windows 的打开方式由assocftype确定。

    3.1 后缀名与打开方式

    尝试性的跑一跑assoc命令,发现其控制着后缀名与打开方式ftype的关系。

    assoc | findstr .js
    

    运行结果:

    .js=JSFile
    .json=VisualStudio.json.14.0
    .jsonld=VisualStudio.jsonld.14.0
    .jsx=VisualStudio.jsx.14.0
    .jsxbin=JSXBINFile
    .jsxinc=JSXINCFile
    

    不难看出.js文件将会通过JSFile这个打开方式去执行。

    3.2 打开方式与执行程序

    类似的,我们也可以运行一下 ftype 命令,其定义了可执行程序以及调用的参数。

    ftype | findstr "JS"
    

    运行结果:

    JSEFile=C:\Windows\System32\WScript.exe "%1" %*
    JSFile=C:\Windows\System32\WScript.exe "%1" %*
    JSXFile="C:\Program Files (x86)\Adobe\Adobe Utilities - CS6\ExtendScript Toolkit CS6\ExtendScript Toolkit.exe" -run "%1"
    

    其中最关键的信息为JSFile=C:\Windows\System32\WScript.exe "%1" %*,含义是通过WScript.exe执行 js 脚本,并将原来的参数传递过去。

    最终node node.js等价于E:\test\node.js node.js

    3.3 怎么破?

    • 发动想象力吧,别再叫node.js了~
    • 是时候切换到 * inx 或者升级到 powershell 了~
    • 如果不介意使用绝对路径的话……

    4. 扩展学习

    操作系统层面通过PATH等环境变量进行资源定位的思路实际上也被广泛应用在各种场景下,下面也举两个常见的栗子说明一下。

    4.1 npm 包定位

    CommonJS 规范中通过require去加载模块时,通过路径补全的策略(详情推荐阅读《深入浅出 Node.js 》),可以省略模块的路径,后缀名,甚至连 /index 也能自动补全。

    4.2 webpack 资源定位

    嘿,resolve 中的 extensions、alias 等思路是否也如出一辙呢?

    5. 总结

    全文原创·此文为随走随记,全文思维略带感情请勿拍砖。

    21 条回复    2017-08-30 09:17:57 +08:00
    shyling
        1
    shyling  
       2017-08-27 20:48:56 +08:00
    没遇到过
    mx1700
        2
    mx1700  
       2017-08-27 20:51:18 +08:00 via Android
    你可能用的假 nodejs
    xcold
        3
    xcold  
    OP
       2017-08-27 21:00:04 +08:00
    嘿嘿 实际上我自己也碰见过,只是把一些同学遇到的问题复盘了一遍~
    NemoAlex
        4
    NemoAlex  
       2017-08-27 21:03:25 +08:00 via iPhone
    总之如果没有特殊的目的,不要用 Windows 来直接做这些语言的开发。实在要用就开个 Linux 的虚拟机,方便多了。
    Miy4mori
        5
    Miy4mori  
       2017-08-27 21:08:19 +08:00 via iPhone
    你估计装了某个魔改 node 在 path 里,which 一下看先,我用 windows10 cmder nvm 三个组合从没遇到这个问题。
    ioc
        6
    ioc  
       2017-08-27 21:31:09 +08:00 via Android
    我刚刚试了下,没有问题
    xcold
        7
    xcold  
    OP
       2017-08-27 21:34:43 +08:00
    @ioc 需要在 cmd 下测试 哈哈
    xqin
        8
    xqin  
       2017-08-27 21:50:46 +08:00
    @xcold 把命令打全, 或者 把你的文件名换个名字.

    你的问题在于,
    1. 你调用 node 没有加 .exe
    2. 你的文件名叫 node.js,当你在命令行中输入 node 的时候, windows 先找到了当前目录下的 node.js, 所以直接调用 与.js 文件所关联和 WSH 来进行执行. 至于 windows 为什么会先找到 node.js 文件然后调用 WSH 来执行,而不是直接用 nodejs 来执行是因为 windows 命令行下在执行程序之前会先在当前目录中找对应的文件名+一些特定的扩展名(比如:exe,bat,cmd,js 之类的). 最简单的命令你在你自己的目录下建一个 123.bat 或者 123.cmd 的文件,然后在命令中进入 到当前目录,只输入 123, 就可以调用它. 同理你有一个 123.js 的时候,你也可以输入 123 来调用 WSH 来解析它.


    正确的方法, 用 `node.exe node.js` 或者 `node 1.js` (1.js 为你的 node.js 改名之后的)
    xcold
        9
    xcold  
    OP
       2017-08-27 22:23:34 +08:00
    感谢各位热心的网友~这贴不是求助帖子哈~你们说的所有的方案估计我都考虑过的~
    ceoimon
        10
    ceoimon  
       2017-08-27 22:51:10 +08:00 via iPhone
    虽然基本不用 CMD(WSL 很好用啊), 还是学习了。
    doubleflower
        11
    doubleflower  
       2017-08-27 23:37:33 +08:00
    折腾这么多还不如把 windows 弃了
    syncher
        12
    syncher  
       2017-08-27 23:40:58 +08:00 via Android
    为啥我也没遇到过?
    FrankFang128
        13
    FrankFang128  
       2017-08-27 23:53:31 +08:00
    Windows 的坑
    lslqtz
        14
    lslqtz  
       2017-08-28 04:56:58 +08:00
    你的环境变量没 node 目录吧。。
    lslqtz
        15
    lslqtz  
       2017-08-28 04:58:55 +08:00
    node.js 安装时默认情况下是不会加到 path 的
    arfaWong
        16
    arfaWong  
       2017-08-28 07:06:03 +08:00 via Android
    没遇到过+ 1
    murmur
        17
    murmur  
       2017-08-28 08:05:29 +08:00
    @lslqtz sure?exe 版的 node 都有加 path 的功能吧
    autoxbc
        18
    autoxbc  
       2017-08-28 10:10:34 +08:00 via iPhone
    好可惜很多人没看懂,用个伪正则说明一下

    系统补全策略
    (当前目录|%PATH%中的目录) node (显式指定扩展名|%PATHEXT%备选扩展名)

    因为 .js 也是备选可执行扩展名
    所以 node 被补全为 .\node.js

    最后变成执行 .\node.js node.js
    至于最后启动的是 WSH 还是文本编辑器就看个人系统的设置了
    SakuraKuma
        19
    SakuraKuma  
       2017-08-28 10:41:18 +08:00
    windows 问题,优先在当前目录找东西。

    当年很多游戏都可以 hijack 也是这个原理。
    用一个游戏加载的同名 dll 放在游戏目录下,会优先加载该 dll,然后进行游戏修改。
    xcold
        20
    xcold  
    OP
       2017-08-28 12:07:24 +08:00
    @autoxbc 主要是我也没有把结论特意的总结,发散思考也挺好的
    lslqtz
        21
    lslqtz  
       2017-08-30 09:17:57 +08:00
    @murmur 我上次装过,没有。.
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2543 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 15:37 · PVG 23:37 · LAX 07:37 · JFK 10:37
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.