Запуск кода командной строки из ноутбука Jupyter

В Ipython Jupyter Notebook есть интересный вариант для выполнения команд командной строки непосредственно из ноутбука. Например:

! mkdir ...
! python file.py

Более того - этот код можно запустить с помощью os:

import os
os.system('cmd command')

но как мне запустить интерактивные команды оболочки. Например:

!conda install package

может потребоваться будущий ввод ([Y]/N) или местоположение папки, но не будет принимать дальнейший ввод.

Ответ 1

Предполагая, что вы спрашиваете об интерактивности, вы можете попробовать.

Если вы когда-нибудь задавались вопросом, как Джупитер знает, когда закончится вывод ячейки: ну, по-видимому, он не знает, он просто выгружает любой захваченный вывод в самую последнюю активную ячейку:

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()

Затем вы можете выполнять команды isssue, например

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, поэтому я не знаю, что происходит, когда Конда задает вопрос.

Удачно, что поле ввода остается в нижней части ячейки (проверено с помощью ls;sleep 10;ls). Невероятно, что поле ввода нуждается в дополнительной записи в конце, чтобы исчезнуть (и это уже "хороший" способ, когда это был простой while(...) - write(bytearray(input())) - flush() 3-лайнер, это было выход с исключением.

Если кто-то хочет попробовать это в Windows, он работает с 'cmd', но я предлагаю использовать hardcoded 'windows-1252' вместо sys.stdin/out.encoding: они говорят UTF-8, но простая команда dir уже производит вывод который не является ни UTF-8, ни ASCII (нерасщепляемое пространство между 3-значными группами в размерах является символом 0xA0). Или просто удалите часть decode (и используйте running=0xA in line)

Ответ 2

Синтаксис !command является альтернативным синтаксисом %system magic, документацию которого можно найти здесь.

Как вы уже догадались, он вызывает os.system и, поскольку os.system работает, нет простого способа узнать, будет ли процесс, который вы будете запускать, потребовать ввода от пользователя. Таким образом, при использовании ноутбука или любого многопроцессорного интерфейса у вас нет возможности динамически вводить данные в программу, которую вы запускаете. (в отличие от вызова input в Python мы можем перехватить).

Поскольку вы явно проявляете интерес к установке пакетов из записной книжки, я предлагаю прочитать следующее от Джейка Ван Дер Пласа, в котором кратко изложено недавнее обсуждение этой темы, и объяснить некоторые сложности, связанные с этим. Конечно, вы можете использовать опцию --yes для conda, но это не гарантирует, что установка с помощью conda всегда будет работать.

Также обратите внимание, что !command - это функция IPython, а не Jupyter.

Ответ 3

Я отправляю это как ответ. Это не очень хороший ответ, но я бы справился с этой проблемой - написать bash script для запуска в фоновом режиме. Я заглянул в "!" оператора, и у него, похоже, не много документации. Я даже не могу найти его в источнике Юпитера. Эта статья:

[Книга Safari о предшественнике Jupyter и компоненте IPython] [1] https://www.safaribooksonline.com/blog/2014/02/12/using-shell-commands-effectively-ipython/

предполагает, что это просто то, что было раньше и, вероятно, навсегда. Если вы не хотите взломать часть Magic Commands Jupyter Notebook и исправить ее самостоятельно.

Тем не менее, учитывая, что с небольшим программированием bash (это просто и сфокусировано) вы можете делать то, что вы пытаетесь сделать, вы можете рассмотреть этот маршрут. Особенно, если вам нужны результаты, чтобы поставить репутацию на него.

Если вы хотите просмотреть сценарии bash с ожидаемым ответом, этот ответ будет тем, что вы ищете: Имейте bash script ответные интерактивные подсказки

Ответ 4

Хорошим вариантом для большинства команд, с которыми я столкнулся, является использование неинтерактивных аргументов. Например. в приведенном выше случае:

conda install package -y

Если вам абсолютно необходимо подать подсказки, вы можете использовать printf hack, например:

printf 'y\n' | conda install package

Это поддерживает несколько входов, вы разделяете их на "\n"