【问题标题】:How to source shell script with npm scripts?如何使用 npm 脚本获取 shell 脚本?
【发布时间】:2019-09-06 04:31:16
【问题描述】:

我的package.json 中有一个脚本:

{
  "scripts": {
    "start": "source run-nvm.sh && ..."
  }
}

但运行source run-nvm.sh && ...yarn start(或npm run start)不同。

为什么?它创建一个子shell。所以我不能改变原始shell的环境,我不能为它export常量或操纵nvm的状态(我不能改变父shell的节点版本)

所以真正的问题

我可以通过不创建子 shell 来执行 yarn/npm 脚本吗? (并使用当前的 shell)

如何使用 npm 脚本获取 shell 脚本?

最终,你可能会尝试通过询问来改变话语:“你为什么不只是 source run-nvm.sh && yarn start”但我不想只是添加一些自定义脚本和复杂性,我希望它在 @ 上自动执行987654329@ / npm start(自动更改节点版本)

真正的问题

它目前可以工作(脚本更改节点的版本并运行应用程序)但由于它是一个子shell,它不会保存 nvm 的状态。因此,在每个yarn start 上,它最初都使用默认版本,然后更改版本,然后启动应用程序,因此它为yarn start 命令增加了大约3-4 秒以进行版本更改。虽然它不应该每次都设置版本,但应该只设置一次,第一次。

【问题讨论】:

  • 您可以使用相同的启动语法获取环境并运行命令,“start”:“source run-nvm.sh; cmd to run ; second cmd ; etc” 其他命令将在其中运行同一个壳。或者您可以按照您的说法在运行纱线之前获取环境。我认为除了在不使用外部命令的情况下自己设置环境变量之外别无选择。
  • @GaryB,是的,我想这里最接近的解决方案是使用 npm/yarn 以外的东西,这对于 npm 脚本的行为会略有不同,并且不会创建子 shell。不过这有点危险,并且需要所有脚本下所有库的信任。我最初来到这里是因为我想自动使用nvm use。我最终使用了一个用于 nvm 的 vscode 插件,所以它只是在内置终端的我的项目文件夹中运行 nvm use,所以无论打开什么项目 - nvm 都会自动切换到正确的 nodejs。不是一个理想的解决方案,因为它只适用于vscode,但还好
  • 我知道它没有被明确要求,但是,bash 解决方案:[ "$(node -v)" != "v10.24.1" ] && source run-nvm.sh && yarn start || yarn start
  • 您可以将所有这些都放在一个 bash 脚本中。 start.sh 并使用bash ./start 运行它,您也可以从 package.json grep 获取节点版本。而不是像我那样硬编码。

标签: node.js shell npm yarnpkg nvm


【解决方案1】:

我今天遇到了这个问题,我找到了一个适合我的简单解决方案。

  1. 使用 vars 创建一个 env 文件

    # cat > .env << EOF
    PORT=8080
    DB_HOST=my-db.host
    DB_PORT=3306
    DB_USER=mysql
    DB_PASS=123456
    EOF
    
  2. package.json 文件上创建一个新条目

    {
      [...]
      "start": "export $(cat .env | egrep -v '#|^$' | xargs) && node production-server/server.js",
      [...]
    }
    
  3. 像往常一样启动应用程序

    # npm run start
    
    > myapp@1.0.2 start /usr/local/myapp
    > export $(cat .env | egrep -v '#|^$' | xargs) && 
    node production-server/server.js
    
    > Ready on http://localhost:8080
    

就是这样。

如果您想知道 export $(cat .env | egrep -v '#|^$' | xargs) 的作用,请继续阅读。

export $(cat .env | egrep -v '#|^$' | xargs)
   |      |           |                 |
   |      |           |                 transform the output in "PORT=8080 DB_HOST=my-db.host DB_PORT=3306 DB_USER=mysql DB_PASS=123456"
   |      |           |
   |      |           filter lines starting with comment or blank line
   |      |
   |      cat the .env file
   |
   save the env on subshell before start the node

【讨论】:

    【解决方案2】:

    发现您对使用 vscode 插件的评论 如果您只想在您的一个项目上的版本不同时更改所有终端的 nvm 版本,那么请查看我的过度杀伤力答案。

    我可能会将所有逻辑放在一个 bash 脚本中,然后分享链接。 ?

    通常node版本存储在package.json中,导致node版本不正确或者npm导致失败。

    // package.json
    
     "engines": {
        "node": ">=10.0.0"
      },
      "scripts": {
    

    通常版本逻辑在 $HOME/.bashrc 文件中由 shell 提供的 bashrc 中。 当创建新的窗口窗口时,它们会重新触发 nvm 使用。

    两种可能性:
    两者都使用脚本:script-to-replace-nvm-version.sh
    # Which replaces in bashrc the "nvm use " line with "nvm use $correctProjectVersion"

    1. 使用 json 包中的启动脚本运行 script-to-replace-nvm-version

    2. 使用 PROMPT_COMMAND 运行 script-to-replace-nvm-version:

    这里使用检查来确保我们在一个 npm 项目中。

    小心使用 PROMPT_COMMAND,因为它每次都会在 ps1 渲染之前运行。

     PROMPT_COMMAND="[ -f ./package.json ] && $HOME/.scriptsourcedbybashrc.sh"
    

    脚本摘要:Nvm 通常来源于 $HOME/.bashrc 中的特定版本。因此,我们可以将 npm 包重写包含 nvm use ... 的行并更新版本。我们甚至可以提示用户您要更新吗?

    从 package.json 检查节点版本

    projectVersion=$(cat ./package.json | grep 'node":' | tr -dc '0-9.')
    echo $projectVersion
    

    从 $HOME/.bashrc 检查节点版本

    nodeVersion=$(cat $HOME/.bashrc | grep 'nvm use' | tr -dc '0-9.')
    echo $nodeVersion
    

    检查 node 和 package.json 版本是否不同

    # bash script, to be sourced by npm run start;
    
    # WIP, needs a few more error checks
    # such as, nvm used in more than one place in your bashrc.
    if
      versions_differ_tell_and_prompt
    fi
    versions_differ_tell_and_prompt() {
      echo "nvm versions differ: ";
      found in project: $projectVersion;
      found in '$HOME/.bashrc': $nodeVersion;
      echo ;
      echo "ignore with any keypress or [uU] to update";
      old_stty_cfg=$(stty -g);
      stty raw -echo ; answer=$(head -c 1) ; stty $old_stty_cfg;
    }
    if echo "$answer" | grep -iq "^u" ;then
        update_file 'nvm use' "nvm use $projectVersion" && source $HOME.bashrc;
    else
        echo "No changes made";
    fi
    update_file() {
      searchLine=$1
      replacementLine=$2
      lineNumber=$(cat $HOME/.bashrc | grep -n $searchLine | cut -f1 -d:)
      cp "$HOME/.bashrc" "$HOME/.bashrc.backup"
      sed -i "$lineNumbers/.*/$replacementLine/" "$HOME/.bashrc"
      source "$HOME/.bashrc"
    }
    unset searchLine
    unset replacementLine
    unset answer
    unset old_stty_cfg
    unset nodeVersion
    unset projectVersion
    

    其他一些有用的链接: https://github.com/md-command-line/ERRORSCREAM/issues/2

    从这里获取大部分逻辑:https://github.com/MichaelDimmitt/git_check_computer/blob/master/git_check_computer.sh

    更正 yarn start 命令以根据找到的锁定文件运行正确的 yarn 或 npm。 https://github.com/MichaelDimmitt/know-your-package-manager

    【讨论】:

    • 哇...我得到了PROMPT_COMMAND 背后的想法,这是一个非常有趣的概念,尽管非常危险。如果 yarn/npm 本身包含 nvm 或者至少他们尝试将命令传播到 nvm 会更酷,并且在看到一些较新的版本时,他们会通过 nvm 自动安装/使用正确的 Node 版本:D
    • 注意:PROMPT_COMMAND 仅适用于当前终端,因此我建议将其放在 .bashrc 中
    【解决方案3】:

    完全猜测,但试试

    {
     "scripts": {
        "start": "bash -c 'source run-nvm.sh && ...'"
      }
    }
    

    【讨论】:

    • npm 和 yarn 在它们自己的层级上创建子shell 的问题,所以不,——无论你在“脚本”中使用什么命令,它都是错误的答案。我认为,如果这不仅仅是一些历史原因并且“它就这样发生了”(这很有可能),那么它应该是一些安全措施,因为您的脚本将获得对所有环境变量的访问权限,而不仅仅是一些默认变量,-例如,这将使 npm 脚本可以访问您的电子邮件(如果您曾经使用过 npm 登录来发布包),因为 npm 将此类信息存储在那里(idk 为什么),并且还有更多其他非常明智的信息。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-12
    • 1970-01-01
    • 2022-08-14
    • 2010-12-24
    • 2019-10-25
    相关资源
    最近更新 更多