web-dev-qa-db-de.com

Befehlszeilencode von Jupyter Notebook aus ausführen

In Ipython Jupyter Notebook gibt es eine interessante Option, um Befehlszeilenanweisungen direkt vom Notebook aus auszuführen. Zum Beispiel:

! mkdir ...
! python file.py

Außerdem kann dieser Code mit os ausgeführt werden:

import os
os.system('cmd command')

aber wie führe ich interaktive Shell-Befehle aus? Zum Beispiel:

!conda install package

erfordert möglicherweise zukünftige Eingaben ([Y]/N) oder Ordnerort, akzeptiert jedoch keine weiteren Eingaben.

11
Dimgold

Angenommen, Sie fragen nach Interaktivität, es gibt etwas, das Sie ausprobieren können.

Wenn Sie sich jemals gefragt haben, wie Jupyter weiß, wann die Ausgabe einer Zelle endet: Nun, das weiß es offensichtlich nicht, es speichert nur die erfassten Ausgaben in die zuletzt aktive Zelle:

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

(Das Beispiel ist absichtlich kürzer als Nizza, da dies nur eine Side-Info ist. Während der 20 Sekunden dauernden Wartezeit haben Sie Zeit, eine andere Zelle zu aktivieren, z. B. durch Ausgeben eines a=6.)

Dies kann verwendet werden, um die Ausgabe eines Konsolencodes auf dem Bildschirm anzuzeigen, während er vom Haupt-Thread gesteuert wird:

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

Dann können Sie Befehle ausgeben, wie

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

und

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

Sogar b'ls\nexit\n' funktioniert, deshalb ist outLoop so lang (eine einfache while(proc.poll() is None)-print(...)-Schleife würde früher enden, als alle Ausgaben erfasst wurden.

Dann kann das Ganze automatisiert werden als:

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

Das funktioniert gut auf https://try.jupyter.org/ , aber offensichtlich wollte ich nicht versuchen, Condapakete dort zu installieren, daher weiß ich nicht, was passiert, wenn Conda eine Frage stellt.

Glücklicherweise bleibt das Eingabefeld am unteren Rand der Zelle (getestet mit ls;sleep 10;ls). Ein Pech ist, dass das Eingabefeld am Ende einen zusätzlichen Eintrag benötigt, um verschwinden zu können (und dies ist bereits der 'Nizza'-Weg, als es eine einfache while(...)-write(bytearray(input()))-flush() 3-Liner war, es wurde mit einer Ausnahme beendet.

Wenn jemand dies unter Windows versuchen möchte, funktioniert es mit 'cmd', aber ich schlage vor, 'windows-1252' anstelle von sys.stdin/out.encoding zu verwenden: Sie sagen UTF-8, aber ein einfacher dir-Befehl erzeugt bereits eine Ausgabe, die weder UTF-8 noch ASCII (der nicht zu unterbrechende Speicherplatz zwischen den dreistelligen Gruppen ist ein 0xA0-Zeichen). Oder entfernen Sie einfach den Teil decode (und verwenden Sie running=0xA in line)

1
tevemadar

!commandsyntax ist eine alternative Syntax der %system-Magie, deren Dokumentation hier gefunden werden kann. 

Wie Sie sich schon gedacht haben, ruft es os.system auf und soweit os.system funktioniert, gibt es keine einfache Möglichkeit zu wissen, ob der von Ihnen ausgeführte Prozess Eingaben des Benutzers erfordert. Wenn Sie also das Notebook oder ein Multi-Process-Frontend verwenden, haben Sie keine Möglichkeit, Eingaben für das laufende Programm dynamisch bereitzustellen. (Im Gegensatz zu einem Aufruf von input in Python können wir can abfangen).

Da Sie explizit Interesse an der Installation von Paketen aus dem Notebook haben, schlage ich vor, das Folgende von Jake Van Der Plas zu lesen, eine Zusammenfassung einer aktuellen Diskussion zu diesem Thema, und erkläre einige der Komplikationen, die sich daraus ergeben. Sie können natürlich die --yes-Option von conda verwenden, aber es kann nicht garantiert werden, dass die Installation mit conda immer funktioniert. 

Beachten Sie auch, dass !command eine IPython - Funktion ist, keine Jupyter-Funktion.

3
Matt

Eine gute Option für die meisten Befehle, die ich gefunden habe, ist die Verwendung von nicht interaktiven Argumenten. Z.B. im obigen Fall:

conda install package -y

Wenn Sie die Eingabeaufforderungen unbedingt eingeben müssen, können Sie printf hack verwenden, z.

printf 'y\n' | conda install package

Dies unterstützt mehrere Eingänge, die Sie durch '\ n' trennen.

0
Ivan

Ich poste dies als Antwort. Es ist keine gute Antwort, aber ich würde das Problem damit umgehen, ein Bash-Skript zu schreiben, das im Hintergrund ausgeführt wird. Ich habe in die "!" Operator und es scheint nicht viel Dokumentation zu haben. Ich kann es nicht einmal in der Jupyter-Quelle finden. Dieser Beitrag:

[Safari-Buch über Jupyter-Vorgänger und Komponente IPython] [1] https://www.safaribooksonline.com/blog/2014/02/12/ using-Shell-commands-effectively-ipython/

legt nahe, dass dies einfach so ist, wie es vorher war und wahrscheinlich für immer sein wird. Es sei denn, Sie möchten in den Magic Commands-Teil des Jupyter Notebooks hacken und ihn selbst reparieren.

In Anbetracht dessen, dass Sie mit einer kleinen Bash-Programmierung (es ist einfach und fokussiert) tun können, was Sie versuchen, können Sie diese Route in Betracht ziehen. Vor allem, wenn Sie genügend Ergebnisse benötigen, um sich einen Namen zu machen.

Wenn Sie sich die Ausführung von Bash-Skripts mit einer erwarteten Antwort ansehen möchten, suchen Sie nach dieser Antwort: Lassen Sie das Bash-Skript interaktive Eingabeaufforderungen beantworten

0
Dylan Brams