【问题标题】:Running interactive command line code from Jupyter notebook从 Jupyter notebook 运行交互式命令行代码
【发布时间】:2017-11-20 02:29:06
【问题描述】:

Ipython Jupyter Notebook 中有一个有趣的选项,可以直接从笔记本执行命令行语句。例如:

! mkdir ...
! python file.py

此外 - 此代码可以使用 os 运行:

import os
os.system('cmd command')

但是我如何运行交互式 shell 命令。例如:

!conda install package

可能需要将来输入 ([Y]/N) 或文件夹位置,但不会接受进一步的输入。

【问题讨论】:

  • 快速提问:您要问的是哪个操作系统?对于这个问题,我认为这很重要。显然,您可以根据需要进入 Bash shell,但如果您使用的是 Windows,则不会有太大区别。
  • 我猜这与他们三个有关,但我对 Mac OS 系统特别感兴趣。
  • Mac 可能有一个 bash shell。我明天再看看。

标签: cmd ipython jupyter


【解决方案1】:

我知道这是一篇旧帖子,但我没有看到提及:

!yes | conda install package

这会通过管道传递一个“y”来响应任何未决的“y/n?”题。显然,请谨慎使用 - 只有当您绝对确定“是”是正确的回答时。

【讨论】:

  • 请考虑形成终端命令或反引号代码。另外,如果您解释一下该命令的作用,那就太好了
【解决方案2】:

!commandsyntax 是%system 魔法的替代语法,可以在here 找到文档。

正如您所猜测的,它正在调用os.system,并且就os.system 的工作而言,没有简单的方法可以知道您将运行的进程是否需要用户输入。因此,当使用笔记本或任何多进程前端时,您无法动态地为正在运行的程序提供输入。 (与 Python 中对 input 的调用不同,我们可以拦截)。

当您明确表示有兴趣从笔记本安装软件包时,我建议您阅读the following from Jake Van Der Plas,这是最近关于该主题的讨论的摘要,并解释了这样做的一些复杂性。你当然可以使用 conda 的--yes 选项,但它不能保证使用 conda 安装总是有效的。

另请注意,!commandIPython 功能,而不是 Jupyter 功能。

【讨论】:

    【解决方案3】:

    假设您询问的是交互性,您可以尝试一下。

    如果您想知道 Jupyter 如何知道单元格的输出何时结束:好吧,它显然不知道,它只是将任何捕获的输出转储到最近活动的单元格中:

    import threading,time
    a=5
    threading.Thread(target=lambda:[print(a),time.sleep(20),print(a)]).start()
    

    (故意比漂亮的例子短,因为这只是侧面信息。在 20 秒的等待运行期间,您有时间激活另一个单元格,可能通过发出 a=6

    这可以用来将一些控制台代码的输出到屏幕上,同时从主线程控制它:

    import sys,threading,subprocess
    
    proc=subprocess.Popen('/bin/sh',stdout=subprocess.PIPE,stdin=subprocess.PIPE,stderr=subprocess.STDOUT)
    pout=proc.stdout
    pin=proc.stdin
    
    def outLoop():
        running=True
        while(running):
            line=pout.readline().decode(sys.stdout.encoding)
            print(line,end='')
            running='\n' in line
        print('Finished')
    
    threading.Thread(target=outLoop).start()
    

    然后你可以发出命令,比如

    pin.write(b'ls -l\n')
    pin.flush()
    

    pin.write(b'exit\n')
    pin.flush()
    

    即使b'ls\nexit\n' 有效,这就是outLoop 如此长的原因(一个简单的while(proc.poll() is None)-print(...) 循环会在它获取所有输出之前完成。

    然后整个事情可以自动化为:

    while(proc.poll() is None):
        inp=bytearray(input('something: ')+'\n',sys.stdin.encoding)
        if(proc.poll() is None):
            pin.write(inp)
            pin.flush()
    

    这在https://try.jupyter.org/ 上运行良好,但显然我不想尝试在那里安装 conda 包,所以我不知道当 conda 提出问题时会发生什么。

    幸运的是,输入字段位于单元格底部(使用ls;sleep 10;ls 测试)。不幸的是,输入字段最后需要一个额外的条目才能消失(这已经是“很好”的方式,当它是一个简单的 while(...)-write(bytearray(input()))-flush() 3-liner 时,它是异常退出。

    如果有人想在 Windows 上尝试这个,它适用于 'cmd',但我建议使用硬编码的 'windows-1252' 而不是 sys.stdin/out.encoding:他们说 UTF-8,但一个简单的 dir 命令已经产生了输出它既不是 UTF-8 也不是 ASCII(大小为 3 位组之间的不可破坏空格是 0xA0 字符)。或者只是删除decode 部分(并使用running=0xA in line

    【讨论】:

    • 感谢您的详细回答。有没有办法让这种机制适用于 Jupyter 的交互式 shell 脚本(根据用户提供的选项工作)?
    【解决方案4】:

    我遇到的大多数命令的好选择是使用非交互式参数。例如。在上述情况下:

    conda install package -y
    

    如果你绝对需要输入提示,你可以使用 printf hack,例如:

    printf 'y\n' | conda install package
    

    这支持多个输入,你用'\n'分隔它们

    【讨论】:

    • 它似乎不像上面的答案那样清楚,但最重要的部分就在那里。要在 Jupyter 笔记本中自动批准 conda 包的安装,您将使用 -y 参数。因此,在(IPython)Jupyter 的单元格中使用 conda 安装包的方法是输入:!conda -y install package
    • 这个问题的答案不完整,但它解决了我的用例,可能还有其他大多数
    【解决方案5】:

    我将其发布为答案。这不是一个好的答案,但我处理问题的方式是编写一个 bash 脚本以在后台运行。我已经调查了“!”运营商,它似乎没有很多文档。我什至无法在 Jupyter 源代码中找到它。本文:

    [关于 Jupyter 前身和组件 IPython 的 Safari 书籍][1]https://www.safaribooksonline.com/blog/2014/02/12/using-shell-commands-effectively-ipython/

    暗示这只是过去的样子,而且可能永远都是这样。除非您想破解 Jupyter Notebook 的 Magic Commands 部分并自行修复。

    也就是说,考虑到通过一点 bash 编程(它简单而专注),您可以做您想做的事情,您可能会考虑使用这条路线。特别是如果您需要足够的结果来赢得声誉。

    如果您想查看运行 bash 脚本的预期响应,则此答案就是您要寻找的答案:Have bash script answer interactive prompts

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-07-20
      • 2019-03-18
      • 1970-01-01
      • 2023-04-05
      • 2019-04-09
      • 1970-01-01
      • 2018-05-11
      相关资源
      最近更新 更多