it-swarm.com.de

Mehrzeilige Anweisungen in der einzeiligen Befehlszeile ausführen?

Ich benutze Python mit -c, um eine Einzeilenschleife auszuführen, d. h .:

$ python -c "for r in range(10): print 'rob'"

Das funktioniert gut. Wenn ich jedoch ein Modul vor der for-Schleife importiere, wird ein Syntaxfehler angezeigt:

$ python -c "import sys; for r in range(10): print 'rob'"
  File "<string>", line 1
    import sys; for r in range(10): print 'rob'
              ^
SyntaxError: invalid syntax

Irgendeine Idee, wie das behoben werden kann?

Es ist mir wichtig, dies als Einzeiler zu haben, damit ich es in ein Makefile aufnehmen kann.

168
user248237

du könntest es tun

echo -e "import sys\nfor r in range(10): print 'rob'" | python

oder ohne Rohre:

python -c "exec(\"import sys\nfor r in range(10): print 'rob'\")"

oder

(echo "import sys" ; echo "for r in range(10): print 'rob'") | python

oder @ SilentGhosts Antwort / @ Crasts Antwort

155
jspcal

dieser Stil kann auch in Makefiles verwendet werden (und tatsächlich wird er ziemlich oft verwendet).

python - <<EOF
import sys
for r in range(3): print 'rob'
EOF

oder

python - <<-EOF
    import sys
    for r in range(3): print 'rob'
EOF

in letzterem Fall werden auch führende Tabulatorzeichen entfernt (und es kann ein gewisses Maß an strukturiertem Outlook erreicht werden)

anstelle von EOF kann ein beliebiges Markierungswort stehen, das nicht im here-Dokument am Anfang einer Zeile erscheint (siehe auch hier Dokumente in der bash-Manpage oder hier ).

80
xorho

Das Problem liegt nicht in der import-Anweisung, sondern in der for-Schleife. Oder genauer gesagt, alles, was vor einem inline Block erscheint.

Zum Beispiel funktionieren diese alle:

python -c "import sys; print 'rob'"
python -c "import sys; sys.stdout.write('rob\n')"

Wenn der Import als Anweisung ein Problem darstellen würde, würde dies funktionieren, aber nicht:

python -c "__import__('sys'); for r in range(10): print 'rob'"

Für Ihr einfaches Beispiel könnten Sie es folgendermaßen umschreiben:

python -c "import sys; map(lambda x: sys.stdout.write('rob%d\n' % x), range(10))"

Lambdas können jedoch nur Ausdrücke ausführen, keine Anweisungen oder mehrere Anweisungen, sodass Sie möglicherweise immer noch nicht in der Lage sind, das zu tun, was Sie tun möchten. Zwischen Generatorausdrücken, Listenverständnis, Lambdas, sys.stdout.write, der eingebauten "Karte" und einer kreativen Zeichenfolgeninterpolation können Sie jedoch einige leistungsstarke Einzeiler erstellen.

Die Frage ist, wie weit möchten Sie gehen und an welchem ​​Punkt ist es nicht besser, eine kleine .py - Datei zu schreiben, die Ihr Makefile stattdessen ausführt?

46
Crast


- Damit diese Antwort auch mit Python 3.x funktioniert, wird print als Funktion aufgerufen: in 3.x funktioniert onlyprint('foo'), während 2.x auch print 'foo' akzeptiert.
- Eine plattformübergreifende Perspektive mit Windows finden Sie unter kxrs hilfreiche Antwort .

In bash, ksh oder zsh:

Verwenden Sie eine ANSI C-quotierte Zeichenfolge ($'...'), Die die Verwendung von \n Ermöglicht. Darstellen von Zeilenumbrüchen, die zu tatsächlichen Zeilenumbrüchen erweitert werden, bevor die Zeichenfolge an python übergeben wird:

python -c $'import sys\nfor r in range(10): print("rob")'

Beachten Sie den \n Zwischen den Anweisungen import und for, um einen Zeilenumbruch zu bewirken.

Um Shell-Variablenwerte zu übergeben, ist es am sichersten, Argumente zu verwenden und auf diese über sys.argv Im Python script:

name='rob' # value to pass to the Python script
python -c $'import sys\nfor r in range(10): print(sys.argv[1])' "$name"

Im Folgenden finden Sie eine Erläuterung der Vor- und Nachteile der Verwendung einer (vorverarbeiteten Escape-Sequenz) in Anführungszeichen gesetzt Befehlszeichenfolge mit eingebettet Shell-Variablenreferenzen.

So arbeiten Sie sicher mit $'...' - Zeichenfolgen:

  • Double\ - Instanzen in Ihrem Original Quellcode.
    • \<char> - Sequenzen - wie in diesem Fall \n, Aber auch die üblichen Verdächtigen wie \t, \r, \b - werden erweitert von $'...' (siehe man printf für die unterstützten Escapes)
  • Escape-Instanzen von ' Als \'.

Wenn Sie POSIX-konform bleiben müssen :

Verwenden Sie printf mit einem Befehlsersetzung :

python -c "$(printf %b 'import sys\nfor r in range(10): print("rob")')"

So arbeiten Sie sicher mit dieser Art von Zeichenfolge:

  • Double\ - Instanzen in Ihrem Original Quellcode.
    • \<char> - Sequenzen - wie in diesem Fall \n, Aber auch die üblichen Verdächtigen wie \t, \r, \b - werden erweitert durch printf (siehe man printf für die unterstützten Escape-Sequenzen).
  • Übergeben Sie einen String in Anführungszeichen an printf %b Und eingebettete einfache Anführungszeichen als'\'' (Sic).

    • Die Verwendung von einfachen Anführungszeichen schützt den Inhalt der Zeichenfolge vor der Interpretation durch Shell.

      • Allerdings können Sie für kurz Python Skripte (wie in diesem Fall) eine Zeichenfolge in doppelten Anführungszeichen verwenden, um Shell Variablenwerte einzubeziehen in Ihre Skripte - solange Sie die damit verbundenen Fallstricke kennen (siehe nächster Punkt); Beispielsweise erweitert die Shell $HOME zum Ausgangsverzeichnis des aktuellen Benutzers. im folgenden Befehl:

        • python -c "$(printf %b "import sys\nfor r in range(10): print('rob is $HOME')")"
      • Die allgemein bevorzugte Methode besteht jedoch darin, Werte aus der Shell über Argumente zu übergeben und in Python über sys.argv Darauf zuzugreifen. Das Äquivalent des obigen Befehls lautet:

        • python -c "$(printf %b 'import sys\nfor r in range(10): print("rob is " + sys.argv[1])')" "$HOME"
    • Wenn Sie eine in doppelten Anführungszeichen Zeichenfolge verwenden, ist dies praktischer. praktisch - Sie können eingebettete einfache Anführungszeichen ohne und eingebettete doppelte Anführungszeichen als \" - verwenden. es macht die Zeichenkette auch der Interpretation durch Shell unterworfen, was die Absicht sein kann oder nicht; Zeichen $ Und ` In Ihrem Quellcode, die nicht für die Shell bestimmt sind, können einen Syntaxfehler verursachen oder die Zeichenfolge unerwartet ändern.

      • Außerdem kann die Shell-eigene \ - Verarbeitung in doppelten Anführungszeichen stören. Um zum Beispiel Python zu erhalten und eine Literalausgabe ro\b zu erzeugen, müssen Sie ihr ro\\b übergeben. mit einem '...' Shell-String und double\ Instanzen erhalten wir:
        python -c "$(printf %b 'import sys\nprint("ro\\\\bs")')" # ok: 'ro\bs'
        Im Gegensatz dazu funktioniert nicht wie beabsichtigt mit einer "..." Shell-Zeichenfolge:
        python -c "$(printf %b "import sys\nprint('ro\\\\bs')")" # !! INCORRECT: 'rs'
        Die Shell interpretiert beide"\b" Und "\\b" Als Literal \b Und benötigt eine schwindelerregende Anzahl von zusätzlichen \ Instanzen, um den gewünschten Effekt zu erzielen:
        python -c "$(printf %b "import sys\nprint('ro\\\\\\\\bs')")"

Um den Code über stdin anstatt über -c Zu übergeben:

Hinweis: Ich konzentriere mich hier auf single - Zeilenlösungen; xorhos Antwort zeigt die Verwendung eines mehrzeiligen here-document - stellen Sie jedoch sicher, dass quote das Trennzeichen ist; B. <<'EOF', es sei denn, Sie möchten ausdrücklich, dass die Shell die Zeichenfolge nach vorne erweitert (was mit den oben genannten Einschränkungen einhergeht).


In bash, ksh oder zsh:

Kombiniere einen ANSI C-quoted string ($'...') Mit einem here-string (<<<...):

python - <<<$'import sys\nfor r in range(10): print("rob")'

- Weist python an, explizit von stdin zu lesen (was standardmäßig der Fall ist). - Ist in diesem Fall optional, aber wenn Sie auch Argumente an die Skripte übergeben möchten, müssen Sie das Argument von einem Skriptdateinamen unterscheiden:

python - 'rob' <<<$'import sys\nfor r in range(10): print(sys.argv[1])'

Wenn Sie POSIX-konform bleiben müssen :

Verwenden Sie printf wie oben, aber mit einer Pipeline, um die Ausgabe über stdin weiterzuleiten:

printf %b 'import sys\nfor r in range(10): print("rob")' | python

Mit einem Argument:

printf %b 'import sys\nfor r in range(10): print(sys.argv[1])' | python - 'rob'
21
mklement0

Irgendeine Idee, wie das behoben werden kann?

Ihr Problem wird dadurch verursacht, dass Python= Anweisungen, getrennt durch ;, Nur "kleine Anweisungen" sein dürfen, die allesamt Einzeiler sind. Aus der Grammatikdatei in den Python docs :

stmt: simple_stmt | compound_stmt
simple_stmt: small_stmt (';' small_stmt)* [';'] NEWLINE
small_stmt: (expr_stmt | del_stmt | pass_stmt | flow_stmt |
             import_stmt | global_stmt | nonlocal_stmt | assert_stmt)

Zusammengesetzte Anweisungen können nicht mit anderen Anweisungen durch Semikolons in derselben Zeile eingefügt werden. Dies mit dem Flag -c Zu tun, ist daher sehr unpraktisch.

Wenn ich Python in einer Bash-Shell-Umgebung demonstriere, finde ich es sehr nützlich, zusammengesetzte Anweisungen einzuschließen. Die einzige einfache Möglichkeit, dies zu tun, ist mit heredocs.

Heredocs

Verwenden Sie ein heredoc (erstellt mit <<) Und Pythons Befehlszeilenschnittstellenoption , -:

$ python - <<-"EOF"
        import sys                    # 1 tab indent
        for r in range(10):           # 1 tab indent
            print('rob')              # 1 tab indent and 4 spaces
EOF

Durch Hinzufügen des - Nach << (Dem <<-) Können Sie Tabs zum Einrücken verwenden (Stackoverflow konvertiert Tabs in Leerzeichen). 8 Leerzeichen eingerückt, um dies zu betonen. Die führenden Tabs werden entfernt.

Sie können es ohne die Tabs mit nur << Tun:

$ python - << "EOF"
import sys
for r in range(10):
    print('rob')
EOF

Das Setzen von Anführungszeichen um EOF verhindert Parameter und arithmetische Erweiterung . Dies macht den heredoc robuster.

Kritik der akzeptierten Antwort (und anderer)

Dies ist nicht sehr lesbar:

echo -e "import sys\nfor r in range(10): print 'rob'" | python

Nicht sehr lesbar und im Fehlerfall zusätzlich schwer zu debuggen:

python -c "exec(\"import sys\\nfor r in range(10): print 'rob'\")"

Vielleicht etwas lesbarer, aber immer noch ziemlich hässlich:

(echo "import sys" ; echo "for r in range(10): print 'rob'") | python

Sie werden eine schlechte Zeit haben, wenn Sie " In Ihrer Python haben:

$ python -c "import sys
> for r in range(10): print 'rob'"

Missbrauche nicht map oder liste Verständnis auf, um for-Schleifen zu erhalten:

python -c "import sys; map(lambda x: sys.stdout.write('rob%d\n' % x), range(10))"

Das sind alles traurig und böse. Tu sie nicht.

13
Aaron Hall

benutze einfach return und tippe es in die nächste Zeile:

[email protected]:~$ python -c "import sys
> for r in range(10): print 'rob'"
rob
rob
...
11
SilentGhost

Das Problem liegt nicht bei der Anweisung import. Das Problem ist, dass die Steuerflussanweisungen in einem python) -Befehl nicht inline funktionieren. Ersetzen Sie diese import -Anweisung durch eine andere, und Sie werden dasselbe Problem feststellen.

Denken Sie darüber nach: python kann unmöglich alles inline. Es verwendet Einrückungen, um den Kontrollfluss zu gruppieren.

8
David Berger

$ python2.6 -c "import sys; [sys.stdout.write('rob\n') for r in range(10)]"

Funktioniert gut. Verwenden Sie "[]", um Ihre for-Schleife einzufügen.

7
Pierre Pericard

Wenn Ihr System Posix.2-kompatibel ist, sollte es das Dienstprogramm printf enthalten:

$ printf "print 'zap'\nfor r in range(3): print 'rob'" | python
zap
rob
rob
rob
7
Alex Martelli

(beantwortet 23. November 10 um 19:48 Uhr) Ich bin kein großer Pythoner - aber ich habe diese Syntax einmal gefunden und vergessen, woher sie stammt. Deshalb dachte ich, ich würde sie dokumentieren:

wenn Sie sys.stdout.write anstelle von print ( verwenden, nimmt sys.stdout.write Argumente in Klammern als Funktion, während print does ), dann können Sie für einen Einzeiler die Reihenfolge des Befehls und des for umkehren, das Semikolon entfernen und einschließen der Befehl in eckigen Klammern, dh:

python -c "import sys; [sys.stdout.write('rob\n') for r in range(10)]"

Habe keine Ahnung, wie diese Syntax in Python :) aufgerufen würde

Hoffe das hilft,

Prost!


(EDIT Di Apr 9 20:57:30 2013) Nun, ich glaube, ich habe endlich herausgefunden, worum es bei diesen eckigen Klammern in Einzeilern geht. sie sind (anscheinend) "Listenverständnisse"; Beachten Sie dies zuerst in Python 2.7:

$ STR=abc
$ echo $STR | python -c "import sys,re; a=(sys.stdout.write(line) for line in sys.stdin); print a"
<generator object <genexpr> at 0xb771461c>

Der Befehl in runden Klammern wird daher als "Generatorobjekt" betrachtet. Wenn wir es "durchlaufen", indem wir next() aufrufen, wird der Befehl in der Klammer ausgeführt (beachte das "abc" in der Ausgabe):

$ echo $STR | python -c "import sys,re; a=(sys.stdout.write(line) for line in sys.stdin); a.next() ; print a"
abc
<generator object <genexpr> at 0xb777b734>

Wenn wir jetzt eckige Klammern verwenden - beachten Sie, dass wir next() nicht aufrufen müssen, damit der Befehl ausgeführt wird, wird er sofort nach der Zuweisung ausgeführt. Eine spätere Untersuchung zeigt jedoch, dass aNone ist:

$ echo $STR | python -c "import sys,re; a=[sys.stdout.write(line) for line in sys.stdin]; print a"
abc
[None]

Dies lässt nicht viel zu suchen übrig, für den Fall der eckigen Klammern - aber ich bin auf diese Seite gestoßen, die meiner Meinung nach erklärt:

Tipps und Tricks zu Python - Erste Ausgabe - Python Tutorials | Dream.In.Code :

Wenn Sie sich erinnern, ist das Standardformat eines einzelnen Zeilengenerators eine Art 'for'-Schleife in eckigen Klammern. Auf diese Weise wird ein iterierbares "One-Shot" -Objekt erstellt, über das Sie nur in eine Richtung iterieren können und das Sie nicht mehr verwenden können, sobald Sie das Ende erreicht haben.

Ein 'Listenverständnis' sieht fast so aus wie ein regulärer Einzeilengenerator, nur dass die regulären Klammern ) - durch eckige Klammern - [] ersetzt werden. Der Hauptvorteil des alistischen Verstehens besteht darin, dass eine 'Liste' erstellt wird und nicht ein 'einmaliges' iterierbares Objekt, so dass Sie darin hin- und herschalten, Elemente hinzufügen, sortieren usw. können.

Und in der Tat ist es eine Liste - es ist nur das erste Element, das keine wird, sobald es ausgeführt wird:

$ echo $STR | python -c "import sys,re; print [sys.stdout.write(line) for line in sys.stdin].__class__"
abc
<type 'list'>
$ echo $STR | python -c "import sys,re; print [sys.stdout.write(line) for line in sys.stdin][0]"
abc
None

Listenverständnisse werden ansonsten in --- (5. Datenstrukturen: 5.1.4. Listenverständnisse - Python v2.7.4-Dokumentation als "Listenverständnisse bieten eine übersichtliche Möglichkeit, Listen zu erstellen "; vermutlich kommt hier die eingeschränkte" Ausführbarkeit "von Listen in Einzeilern ins Spiel.

Nun, ich hoffe, dass ich hier nicht zu daneben liege ...

EDIT2: und hier ist eine einzeilige Befehlszeile mit zwei nicht verschachtelten for-Schleifen; beide in eckigen Klammern "Listenverständnis" eingeschlossen:

$ echo $STR | python -c "import sys,re; a=[sys.stdout.write(line) for line in sys.stdin]; b=[sys.stdout.write(str(x)) for x in range(2)] ; print a ; print b"
abc
01[None]
[None, None]

Beachten Sie, dass die zweite "Liste" b jetzt zwei Elemente enthält, da die for-Schleife explizit zweimal ausgeführt wurde. In beiden Fällen war das Ergebnis von sys.stdout.write() (anscheinend) None.

4
sdaau

single/double quotes und backslash überall:

$ python -c 'exec("import sys\nfor i in range(10): print \"bob\"")'

Viel besser:

$ python -c '
> import sys
> for i in range(10):
>   print "bob"
> '
4
kev

Diese Variante ist am portabelsten, um mehrzeilige Skripte unter Windows und * nix, py2/3, ohne Pipes in die Befehlszeile zu stellen:

python -c "exec(\"import sys \nfor r in range(10): print('rob') \")"

(Keines der anderen hier bisher gezeigten Beispiele hat dies getan.)

Ordentlich unter Windows ist:

python -c exec"""import sys \nfor r in range(10): print 'rob' """
python -c exec("""import sys \nfor r in range(10): print('rob') """)

Ordentlich auf Bash/* Nix ist:

python -c $'import sys \nfor r in range(10): print("rob")'

Diese Funktion verwandelt jedes mehrzeilige Skript in einen portablen Command-One-Liner:

def py2cmdline(script):
    exs = 'exec(%r)' % re.sub('\r\n|\r', '\n', script.rstrip())
    print('python -c "%s"' % exs.replace('"', r'\"'))

Verwendung:

>>> py2cmdline(getcliptext())
python -c "exec('print \'AA\tA\'\ntry:\n for i in 1, 2, 3:\n  print i / 0\nexcept:\n print \"\"\"longer\nmessage\"\"\"')"

Eingabe war:

print 'AA   A'
try:
 for i in 1, 2, 3:
  print i / 0
except:
 print """longer
message"""
3
kxr

Dieses Skript bietet eine Perl-ähnliche Befehlszeilenschnittstelle:

Pyliner - Skript zum Ausführen von willkürlichem Python Code in der Befehlszeile (Python-Rezept)

2
Drew Gulino

Ich wollte eine Lösung mit folgenden Eigenschaften:

  1. Lesbar
  2. Lesen Sie stdin, um die Ausgabe anderer Tools zu verarbeiten

Beide Anforderungen wurden in den anderen Antworten nicht angegeben. Lesen Sie also wie Sie stdin lesen, während Sie alles in der Befehlszeile tun:

grep special_string -r | sort | python3 <(cat <<EOF
import sys
for line in sys.stdin:
    tokens = line.split()
    if len(tokens) == 4:
        print("%-45s %7.3f    %s    %s" % (tokens[0], float(tokens[1]), tokens[2], tokens[3]))
EOF
)
1
Nick

Wenn Sie stdin nicht berühren und simulieren möchten, als hätten Sie "python cmdfile.py" übergeben, können Sie aus einer Bash-Shell Folgendes tun:

$ python  <(printf "Word=raw_input('Enter Word: ')\nimport sys\nfor i in range(5):\n    print(Word)")

Wie Sie sehen, können Sie stdin zum Lesen von Eingabedaten verwenden. Intern erstellt die Shell die temporäre Datei für den Inhalt des Eingabebefehls.

0
Thava

es gibt noch eine weitere Option: sys.stdout.write gibt None zurück, wodurch die Liste leer bleibt

 cat somefile.log | python -c "sys importieren; [Zeile für Zeile in sys.stdin, wenn sys.stdout.write (Zeile * 2)]" 
0
user993533

Wenn ich das tun musste, benutze ich

python -c "$(echo -e "import sys\nsys.stdout.write('Hello World!\\\n')")"

Beachten Sie den dreifachen Backslash für die Newline in der Anweisung sys.stdout.write.

0
Devilholk