【问题标题】:How to run a system command from VSCode extension如何从 VSCode 扩展运行系统命令
【发布时间】:2017-08-17 20:24:30
【问题描述】:

我正在尝试创建一个简单的 VSCode 扩展,以便在打开文件夹时运行一组命令。基本上这些命令将设置我们的开发环境。我已经开始创建样板并运行了 VSCode 提供的示例,但我不清楚如何运行系统命令。感谢任何帮助或向我指出有关此主题的一些文档。

【问题讨论】:

    标签: visual-studio-code vscode-extensions


    【解决方案1】:

    您的扩展环境可以访问 node.js 库,因此您可以使用 child_process 或任何辅助库来执行命令:

    const cp = require('child_process')
    cp.exec('pwd', (err, stdout, stderr) => {
        console.log('stdout: ' + stdout);
        console.log('stderr: ' + stderr);
        if (err) {
            console.log('error: ' + err);
        }
    });
    

    【讨论】:

    • 这是我们应该做的事情吗?没有 VSCode 优雅的 API 吗?如果用户突然关闭 VSCode 会怎样?这意味着也许我的扩展外部进程将永远运行?
    • 不,它是一个child_process,在这种情况下,当VSCode进程终止时,任何子进程也会终止。
    • @MatBee 遗憾的是,这不是子进程的工作方式。见:stackoverflow.com/questions/8533377/…
    • @MattBierner 它变得一团糟,最终变得非常非跨平台,正如您可以在您自己链接的文档中阅读的那样,引用:“在非 Windows 平台上,[...]子进程可以在父进程退出后继续运行,无论它们是否分离。”
    【解决方案2】:

    如果您需要用户完全可观察和可控的过程,则另一种选择是使用Terminal API,这是最佳选择。

    最大的缺点:Terminal API 还没有提供一种方法来检查它内部运行的进程。

    如果您真的想在终端中运行该进程,那么目前安全地执行此操作的唯一方法是使用两层方法,您可以在其中启动一个包装进程,然后启动并观察实际进程(通过命令行参数获取)。

    我们自制的TerminalWrapper

    我们自己尝试过。

    • 在我们的第一种方法中,包装器使用socket.io 连接,允许与扩展程序进行通信和控制。

    • 在我们的第二种方法中,我们简化并改为使用 bash -c(非交互式 shell)创建终端,并使用文件观察程序来获取结果。这种方式更容易,但在该过程完成后,用户将无法使用终端窗口(因为它是非交互式的)。更不容易出错,并且不需要实现 socket.io 依赖。

    实现细节

    1. 在我们的扩展程序中,我们使用 TerminalWrapper 在包装进程内运行命令,并等待文件包含结果。
    2. 包装进程是here。它将结果写入文件。
    3. 用法示例here:
    const cwd = '.';
    const command = `node -e "console.log('hi!');"`;
    
    const { code } = await TerminalWrapper.execInTerminal(cwd, command, {}).waitForResult();
    if (code) {
      const processExecMsg = `${cwd}$ ${command}`;
      throw new Error(`Process failed with exit code ${code} (${processExecMsg})`);
    }
    

    第二种方法的最大缺点是我们现在需要bash 存在,但是(i)我们确实有一个依赖检查器,如果您不这样做,它会警告您并解释如何获取它,并且(ii ) 使用统一的 shell,使运行命令变得更加容易,因为我们现在拥有非常强大的统一功能集,我们知道我们可以依赖,而不仅仅是能够使用通用的命令执行语法,并且 (iii) 我们甚至可以运行 *.sh 文件而无需担心。

    介绍:VSCode 终端 API

    以下所有图像和摘录都是从他们的官方示例存储库直接复制和粘贴的:

    创建终端并在其中运行命令

    context.subscriptions.push(vscode.commands.registerCommand('terminalTest.createAndSend', () => {
        const terminal = vscode.window.createTerminal(`Ext Terminal #${NEXT_TERM_ID++}`);
        terminal.sendText("echo 'Sent text immediately after creating'");
    }));
    

    终端激活事件

    vscode.window.onDidChangeActiveTerminal(e => {
        console.log(`Active terminal changed, name=${e ? e.name : 'undefined'}`);
    });
    

    TerminalQuickPickItem

    function selectTerminal(): Thenable<vscode.Terminal | undefined> {
        interface TerminalQuickPickItem extends vscode.QuickPickItem {
            terminal: vscode.Terminal;
        }
        const terminals = <vscode.Terminal[]>(<any>vscode.window).terminals;
        const items: TerminalQuickPickItem[] = terminals.map(t => {
            return {
                label: `name: ${t.name}`,
                terminal: t
            };
        });
        return vscode.window.showQuickPick(items).then(item => {
            return item ? item.terminal : undefined;
        });
    }
    

    ...还有更多!...

    【讨论】:

    • 有没有办法知道正在运行的终端是否忙?谢谢
    • 终端 API 目前非常有限。它不允许您查询有关其状态的任何内容,除了它是否打开、它的 PID、尺寸和您在上面的屏幕截图中看到的其他内容。您甚至可以使用onDidWriteTerminalData 事件获取输出(但是这可能永远不会成为here 所讨论的稳定API 的一部分)。遗憾的是,没有办法知道终端内是否或当前正在运行什么,除非您按照我上面的建议将命令包装在观察者应用程序中。
    【解决方案3】:

    我所做的是创建一个基于 Promise 的实用函数来使用 child_process 运行所有 shell 命令

    import * as cp from "child_process";
    
    const execShell = (cmd: string) =>
        new Promise<string>((resolve, reject) => {
            cp.exec(cmd, (err, out) => {
                if (err) {
                    return reject(err);
                }
                return resolve(out);
            });
        });
    

    获取当前目录

    const currentDir = await execShell('pwd');
    

    获取当前 git 分支名称

    const branchName = await execShell('git rev-parse --abbrev-ref HEAD');
    

    【讨论】:

    • 哇,这真的很完整。谢谢,帮了大忙。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-06-21
    • 1970-01-01
    • 2019-07-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多