nvm 在 bash 中有效,但在 Python 脚本中执行时无效

问题描述 投票:0回答:1

安装nvm后,一直正常工作,直到我需要使用nvm用python远程连接服务器。切换node版本没有报错也没有成功,所以我在服务器上写了一个脚本进行测试:

测试.py:

import os
res = os.popen("nvm use v14.2.0")
# res = os.popen("~/nvm/nvm.sh use v14.2.0")
print(res)
print(res.read())

输出:

$ python3 test.py
<os._wrap_close object at 0x7f00acdc4ac8>
/bin/sh: nvm: command not found

nvm安装路径为

~/nvm
~/nvm/nvm.sh

如果我直接在服务器上执行

nvm -v
,输出是
0.39.1
。当我直接执行 nvm 时,它工作正常。为什么从Python脚本执行时不起作用?

在nvm安装目录下执行

sh ./nvm.sh
没有任何输出。

-rwxrwxr-x 1 asd asd 139220 Mar 11 15:57 nvm.sh

$ cd ~/nvm
$ sh ./nvm.sh
$

我的

.bashrc
文件:

# .bashrc

# Source global definitions
if [ -f /etc/bashrc ]; then
        . /etc/bashrc
fi

# Uncomment the following line if you don't like systemctl's auto-paging feature:
# export SYSTEMD_PAGER=

# User specific aliases and functions
source ~/nvm/nvm.sh

核心问题是

nvm
可以在命令行执行,但是找不到nvm可执行文件所在的路径。或者
nvm.sh
是可执行文件,但当我执行此文件时没有任何反应。

注意:当我输入

which nvm
时,它输出:

/usr/bin/which: no nvm in (/home/pyer/nvm/versions/node/v14.2.0/bin:/usr/local/nodejs/bin:/usr/local/bin :/usr/bin:/usr/local/sbin:/usr/sbin:/usr/lib/golang/bin:/usr/local/apache-maven-3.8.4/bin)

更新

上次测试后,代码执行不再报错或阻塞,但又发现了新的问题。代码好像没有执行成功,我重写了一个测试脚本

测试.py:

import subprocess

def test():
    # p = subprocess.Popen(['bash', '-c', '-i', 'nvm use v14.2.0'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, executable='/usr/bin/bash')
    p = subprocess.Popen(['bash', '-c', '. ~/nvm/nvm.sh; nvm use v14.2.0'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, executable='/usr/bin/bash')
    out, err = p.communicate()
    print(out.decode('utf-8'))

    # p = subprocess.Popen(['bash', '-c', '-i', 'nvm list'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, executable='/usr/bin/bash')
    p = subprocess.Popen(['bash', '-c', '. ~/nvm/nvm.sh; nvm list'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, executable='/usr/bin/bash')
    out, err = p.communicate()
    print(out.decode('utf-8'))

test()

输出:

$ python3 test.py 
Now using node v14.2.0 (npm v6.14.4)

         v8.9.0
->     v10.24.1
       v12.10.0
        v14.2.0
        v17.5.0
         system
default -> v14.2.0
iojs -> N/A (default)
unstable -> N/A (default)
node -> stable (-> v17.5.0) (default)
stable -> 17.5 (-> v17.5.0) (default)
lts/* -> lts/gallium (-> N/A)
lts/argon -> v4.9.1 (-> N/A)
lts/boron -> v6.17.1 (-> N/A)
lts/carbon -> v8.17.0 (-> N/A)
lts/dubnium -> v10.24.1
lts/erbium -> v12.22.10 (-> N/A)
lts/fermium -> v14.19.0 (-> N/A)
lts/gallium -> v16.14.1 (-> N/A)

显示正在使用

v14.2.0
,但实际上是默认的
v10.24.1
。我发现
nvm
与其他命令有点不同。在我的机器上,其他命令都有可执行文件。但是nvm加载了
.bashrc
/
.zshrc
,所以我有两个猜测。 1是因为python调用的bash没有加载
.bashrc
,所以python执行时的nvm和命令行执行的nvm不一样,可能范围不一样? 2是因为python的子进程调用了一些脚本,这些脚本为了‘安全’会在沙箱中执行,比如
nvm

python nvm
1个回答
3
投票

错误非常明显:系统找不到

nvm
命令。这可能是因为子进程中的搜索路径与 shell 中的搜索路径不同。

文档对此给出了以下建议:

警告:为了获得最大可靠性,请使用可执行文件的完全限定路径。要在

PATH
上搜索不合格名称,请使用
shutil.which()
。在所有平台上,推荐通过
sys.executable
来再次启动当前的 Python 解释器,并使用
-m
命令行格式来启动已安装的模块。

解析可执行文件的路径(或参数的第一项)是平台相关的。 (...)

您也可以更改命令以包含完整路径。所以类似:

os.popen("/usr/bin/nvm use v14.2.0")

要找出正确的路径,请在 shell 中输入

which nvm
。这应该打印您的 nvm 可执行文件的完整路径。

更新

虽然上述情况一般适用于所有应用程序,但问题是为什么在您的路径中找不到

nvm
可执行文件。 documentation 解释说应该首先调用
~/nvm/nvm.sh
脚本来“加载 nvm”。我原本怀疑这只是设置 PATH 变量,以便可以找到
nvm
可执行文件,但是查看
nvm.sh
,似乎 nvm 命令实际上不是可执行文件,而是 shell 函数。这就是为什么当尝试从 Python 脚本执行它时找不到它。

根据这个答案,您应该能够运行以下命令:

subprocess.Popen(['bash', '-c', '. ~/nvm/nvm.sh; nvm'])

更新2

请注意,

nvm
命令似乎旨在在命令行上使用。该脚本所做的事情之一是更改环境变量,例如 PATH。例如,请参见此处。如果您输入
nvm use v14.2.0
,它就会选择不同的版本。

如果你按照你的方式从Python调用它,它不会有任何效果。它将更改当前环境中的

PATH
变量,然后将其关闭。第二次调用
subprocess.Popen
将创建一个新环境,在该环境中将再次使用默认版本。不过,以下命令应该按预期工作,因为两个
nvm
命令现在将在同一个 shell 进程中执行:

subprocess.Popen(['bash', '-c', '. ~/nvm/nvm.sh; nvm use v14.2.0; nvm list'],
                 stdout=subprocess.PIPE, stderr=subprocess.PIPE,
                 executable='/usr/bin/bash')

但是,您应该真正考虑一下您想要在这里实现的目标。为什么从 Python 脚本中调用

nvm
?如果您只是执行它来设置正确的版本,您不妨将该逻辑包含在您的 Python 脚本中。如果您只是调用一些
nvm
命令,您可以轻松地在 bash 脚本中执行此操作。但是,如果您确实愿意,可以这样做,但您必须记住每次调用
subprocess.Popen()
都会导致一个新环境,类似于启动到该服务器的新远程会话。

© www.soinside.com 2019 - 2024. All rights reserved.