Exécution des instructions multi-lignes Python dans la ligne de commande d'une ligne

J'utilise Python avec -c pour exécuter une boucle à une doublure, i.e.:

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

ça marche très bien. Cependant, si j'importe un module avant la boucle for, j'obtiens une erreur de syntaxe:

$ 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

une idée de comment cela peut être corrigé?

il est important pour moi d'avoir ceci comme une doublure pour que je puisse l'inclure dans un Makefile.

141
demandé sur Aaron Hall 2010-01-11 20:11:27

17 réponses

vous pourriez le faire

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

ou w/out tuyaux:

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

ou

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

ou @ SilentGhost la réponse de / @Crast la réponse de

125
répondu jspcal 2017-05-23 12:10:44

ce style peut aussi être utilisé dans les makefiles (et en fait il est utilisé assez souvent).

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

ou

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

dans ce dernier cas, "151970920 onglet" caractères sont supprimés (et certains structuré outlook peut être atteint)

au lieu de EOF peut contenir n'importe quel mot repère qui n'apparaît pas dans le document ici au début d'une ligne (Voir aussi ici les documents dans la page de manuel de bash ou ici ).

71
répondu xorho 2015-03-25 01:01:15

le problème n'est pas en fait avec la déclaration d'importation, c'est avec n'importe quoi étant avant la boucle for. Ou plus précisément, tout ce qui apparaît devant un bloc inlined.

par exemple, tout cela fonctionne:

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

si l'importation étant une déclaration était un problème, cela fonctionnerait, mais ce n'est pas le cas:

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

pour votre exemple de base, vous pouvez le réécrire comme ceci:

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

Cependant, lambdas ne peut exécuter que des expressions, pas des instructions ou des instructions multiples, de sorte que vous pouvez toujours être incapable de faire la chose que vous voulez faire. Cependant, entre les expressions du générateur, list comprehension, lambdas, sys.la sortie standard stdout.write, le" map " builtin, et un peu d'interpolation de chaîne créative, vous pouvez faire quelques puissants one-liners.

la question Est, jusqu'où voulez-vous aller, et à quel point n'est-il pas préférable d'écrire un petit .py fichier qui votre makefile s'exécute à la place?

38
répondu Crast 2010-01-11 17:39:30



- Pour que cette réponse fonctionne avec Python 3.x aussi, print est appelé comme une fonction : en 3.x, seulement print('foo') fonctionne, tandis que 2.x accepte aussi print 'foo' .

- Pour une perspective multiplateforme qui inclut Windows , voir la réponse utile de kxr .

bash , ksh , ou zsh :

utilisez une chaîne de caractères ANSI C-quoted ( $'...' ), qui permet d'utiliser \n pour représenter les nouvelles lignes qui sont étendues aux nouvelles lignes réelles avant que la chaîne soit passée à python :

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

Note la \n entre le import et for déclarations pour effectuer un saut de ligne.

à passez les valeurs variables de shell à une telle commande, il est plus sûr d'utiliser arguments et d'y accéder via sys.argv dans le script Python:

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

voir ci-dessous pour une discussion sur les avantages et les inconvénients de l'utilisation d'une (séquence d'évacuation pré-traitée) double citation chaîne de commande avec intégré shell-les références à des variables.

pour travailler en toute sécurité avec $'...' cordes:

  • Double \ "instances dans votre code source original .
    • \<char> séquences-telles que \n dans ce cas, mais aussi les suspects habituels tels que \t , \r , \b - sont élargis par $'...' (voir man printf pour les évasions supportées)
  • Escape ' instances comme \' .

si vous devez rester conforme à POSIX :

Utiliser printf avec un la substitution de commande :

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

de travailler en toute sécurité avec ce type de chaîne de caractères:

  • Double \ "instances dans votre code source original .
    • \<char> séquences-telles que \n dans ce cas, mais aussi les suspects habituels tels que \t , \r , \b - sont étendues par printf (voir man printf pour les séquences d'échappement supportées).
  • Pass "1519780920 d'une" cité chaîne printf %b et escape incorporé des guillemets simples comme '\'' (sic).

    • l'utilisation de guillemets simples protège le contenu de la chaîne de l'interprétation par le shell .

      • qui dit, pour court scripts Python (comme dans ce case) vous pouvez utiliser une chaîne de caractères double - quoted pour incorporer les valeurs variables shell dans vos scripts-aussi longtemps que vous êtes conscient des pièges associés (voir point suivant); par exemple, le shell étend $HOME à la dir d'origine de l'utilisateur courant. dans la commande suivante:

        • python -c "$(printf %b "import sys\nfor r in range(10): print('rob is $HOME')")"
      • cependant, l'approche généralement préférée est de passer des valeurs de shell via arguments , et y accéder via sys.argv en Python; l'équivalent de la commande ci-dessus est:

        • python -c "$(printf %b 'import sys\nfor r in range(10): print("rob is " + sys.argv[1])')" "$HOME"
    • tout en utilisant un double-coté la chaîne est plus pratique - il vous permet d'utiliser des guillemets simples emboîtés et des guillemets doubles emboîtés comme \" - il rend également la chaîne sujette à l'interprétation par le shell , qui peut ou ne peut pas être l'intention; $ et ` caractères dans votre code source qui ne sont pas destinés pour le shell peut causer une erreur de syntaxe ou de modifier la chaîne de façon inattendue.

      • de plus, le propre traitement de \ du shell dans des chaînes de caractères à double citation peut gêner; par exemple, pour obtenir Python pour produire la sortie littérale ro\b , vous devez passer ro\b à elle; avec une '...' chaîne shell et doublé \ instances, nous obtenons:

        python -c "$(printf %b 'import sys\nprint("ro\\bs")')" # ok: 'ro\bs'

        En revanche, cela ne pas travailler comme prévu avec une "..." chaîne shell:

        python -c "$(printf %b "import sys\nprint('ro\\bs')")" # !! INCORRECT: 'rs'

        Le shell interprète à la fois "\b" et "\b" littérale \b , nécessitant une vertigineuse du nombre de supplémentaires \ instances pour obtenir l'effet désiré:

        python -c "$(printf %b "import sys\nprint('ro\\\\bs')")"

à passer le code via stdin plutôt que -c :

Note: en se concentrant sur single -line solutions here; xorho's answer montre comment utiliser une ligne multiple here-document -soyez sûr de quote the delimiter, however; e.g., <<'EOF' , à moins que vous ne vouliez explicitement que le shell étende la chaîne à l'avant (qui vient avec le mises en garde notées ci - dessus).


Dans bash , ksh , ou zsh :

combinez un ANSI C-chaîne Citée ( $'...' ) avec un here-string ( <<<... ):

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

- indique python explicitement à lire à partir de stdin (ce qu'il fait par défaut). - est facultatif dans ce cas, mais si vous voulez aussi passer arguments à les scripts, vous n'avez besoin de lever l'ambiguïté de l'argument à partir d'un script nom de fichier:

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

Si vous devez rester POSIX :

utiliser printf comme ci-dessus, mais avec un pipeline afin de passer sa sortie via stdin:

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

avec un argument:

printf %b 'import sys\nfor r in range(10): print(sys.argv[1])' | python - 'rob'
15
répondu mklement0 2017-05-23 12:18:22

utilisez simplement return et tapez - le sur la ligne suivante:

user@host:~$ python -c "import sys
> for r in range(10): print 'rob'"
rob
rob
...
11
répondu SilentGhost 2010-01-11 17:14:19

une idée de la façon dont cela peut être réparé?

votre problème est créé par le fait que les instructions Python, séparées par ; , ne sont autorisées à être que de "petites instructions", qui sont toutes des lignes uniques. Extrait du fichier grammatical 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)

les déclarations composées ne peuvent pas être incluses sur la même ligne que les autres déclarations via point-virgule - donc faire cela avec le drapeau -c devient très incommode.

lors de la démonstration de Python dans un environnement shell bash, je trouve très utile d'inclure des déclarations composées. La seule façon simple de le faire est avec heredocs.

Heredocs

utilisez un heredoc (créé avec << ) et option d'interface en ligne de commande de Python 1519250920" , - :

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

ajouter le - après << (le <<- ) vous permet d'utiliser tabs pour indenter (Stackoverflow convertit tabs en espaces, donc j'ai indenté 8 espaces pour souligner ceci). Les onglets principaux seront dépouillés.

Vous pouvez le faire sans les onglets avec juste << :

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

Mettre des guillemets autour de EOF empêche paramètre et expansion arithmétique . Cela rend l'hérédoc plus robuste.

Critique de la accepté de répondre (et les autres)

ce n'est pas très lisible:

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

pas très lisible, et en plus difficile à déboguer en cas d'erreur:

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

peut-être un peu plus lisible, mais quand même assez laid:

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

vous passerez un mauvais moment si vous avez " dans votre python:

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

N'abusez pas des map ou interprétations de la liste pour obtenir des boucles:

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

tout cela est triste et mauvais. Ne les faites pas.

10
répondu Aaron Hall 2016-07-07 18:20:43

le problème n'est pas avec l'énoncé import . Le problème est que les instructions de flux de contrôle ne fonctionnent pas inline dans une commande python. Remplacez cette déclaration import par toute autre déclaration et vous verrez le même problème.

pensez - y: python ne peut pas tout aligner. Il utilise l'indentation pour grouper contrôle-Flux.

7
répondu David Berger 2010-01-11 17:19:27

si votre système est Posix.2 conforme il doit fournir le printf utilitaire:

$ printf "print 'zap'\nfor r in range(3): print 'rob'" | python
zap
rob
rob
rob
7
répondu Alex Martelli 2010-01-12 01:33:45

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

fonctionne très bien. Utilisez "[ ]" aux commandes de votre boucle.

7
répondu Pierre Pericard 2012-01-11 13:40:49

(réponse Nov 23 '10 à 19:48) Je ne suis pas vraiment un grand Pythoner - mais j'ai trouvé cette syntaxe une fois, j'ai oublié d'où, alors j'ai pensé que je devais la documenter:

si vous utilisez sys.stdout.write au lieu de print ( la différence étant, sys.stdout.write prend les arguments comme une fonction, entre parenthèses - alors que print ne ), alors pour un one-liner, vous pouvez vous en sortir en inversant l'ordre de la commande et le for , en enlevant le point-virgule, et en plaçant la commande entre crochets, i.e.:

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

N'ont aucune idée de comment cette syntaxe serait appelée en Python:)

Espère que cette aide,

santé!


(EDIT mar avr 9 20:57:30 2013) Eh bien, je pense que j'ai finalement trouvé ce que ces crochets dans les mono-liners sont au sujet; ils sont " Liste compréhensions" (apparemment); première note en 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>

ainsi la commande entre parenthèses/parenthèses est considérée comme un "objet générateur"; si nous "itérons" à travers elle en appelant next() - alors la commande à l'intérieur de la parenthèse sera exécutée (notez le "abc" dans la sortie):

$ 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>

si nous utilisons maintenant les crochets-notez que nous n'avons pas besoin d'appeler next() pour que la commande soit exécutée, elle s'exécute immédiatement après l'affectation; toutefois, une inspection ultérieure révèle que a est None :

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

cela ne laisse pas beaucoup d'info à chercher, pour le cas des crochets - mais je suis tombé sur cette page qui je pense explique:

Python Trucs Et Astuces – Première Édition - Python Tutoriels | Rêve.Dans.Code :

si vous vous rappelez, le format standard d'un générateur de ligne est une sorte de une ligne "pour" boucle à l'intérieur des crochets. Cela va produire un objet itérable "one-shot" qui est un objet que vous pouvez itérer dans une seule direction et que vous ne pouvez pas réutiliser une fois que vous atteignez la fin.

Une compréhension de liste " est presque le même comme régulière d'un générateur de ligne, sauf que les parenthèses - ( ) - sont remplacées par des crochets - [ ]. L'avantage majeur de la compréhension aliste est que produit une "liste", plutôt que un objet "one-shot" itérable, de sorte que vous pouvez aller et venir à travers elle, ajouter des éléments, trier, etc.

Et en effet, c'est une liste - c'est juste son premier élément devient néant dès qu'elle est exécutée:

$ 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

les interprétations de listes sont par ailleurs documentées dans 5. Structure Des Données: 5.1.4. Liste compréhensions-Python v2.7.4 la documentation as " liste des interprétations fournissent un moyen concis de créer listes"; vraisemblablement, c'est là que la "exécutabilité" limitée des listes entre en jeu dans les monolithes.

Eh bien, j'espère que je ne suis pas trop hors de propos ici ...

EDIT2: et voici une ligne de commande à ligne unique avec deux boucles for-loops non imbriquées; toutes deux placées entre crochets dans "list comprehension":

$ 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]

notez que la seconde "liste " b comporte désormais deux éléments, puisque sa boucle for a été explicitement lancée deux fois; toutefois, le résultat de sys.stdout.write() dans les deux cas était (apparemment) None .

4
répondu sdaau 2013-04-09 19:32:39

cette variante est la plus portable pour mettre des scripts multi-ligne sur la ligne de commande sur Windows et * Nix, py2/ 3, sans pipes:

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

(aucun autre exemple vu ici jusqu'à présent)

Soigné sous Windows est:

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

Soigné sur bash/*nix est:

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

cette fonction fait de n'importe quel script multiligne une ligne de commande portable:

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

Utilisation:

>>> 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\"\"\"')"

Entrée a été:

print 'AA   A'
try:
 for i in 1, 2, 3:
  print i / 0
except:
 print """longer
message"""
3
répondu kxr 2016-02-26 15:11:16

single/double quotes et backslash partout:

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

beaucoup mieux:

$ python -c '
> import sys
> for i in range(10):
>   print "bob"
> '
2
répondu kev 2011-12-11 08:17:12

ce script fournit une interface en ligne de commande semblable à Perl:

Pyliner-Script pour exécuter du code Python arbitraire sur la ligne de commande (recette Python)

2
répondu Drew Gulino 2012-10-29 11:05:08

il y a une autre option, sys.la sortie standard stdout.write retourne None, qui garde la liste vide

cat somefile.log|python -c "import sys;[line for line in sys.stdin if sys.stdout.write(line*2)]"
0
répondu user993533 2014-06-06 04:30:59

j'ai écrit une simple page web pour ça. Vous pouvez y coller votre code multi-ligne et il est converti en une déclaration simple-ligne de travail.

Python Seul Convertisseur De Ligne

0
répondu jagttt 2015-04-10 15:58:31

si vous ne voulez pas toucher stdin et simuler comme si vous aviez passé "python cmdfile.py", vous pouvez faire ce qui suit à partir d'un shell bash:

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

comme vous pouvez le voir, il vous permet d'utiliser stdin pour la lecture des données d'entrée. En interne, le shell crée le fichier temporaire pour le contenu de la commande d'entrée.

0
répondu Thava 2016-07-07 18:24:46

quand j'ai besoin de faire ceci, j'utilise

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

Notez le triple antislash pour la nouvelle ligne dans le sys.la sortie standard stdout.Ecrivez la déclaration.

0
répondu Devilholk 2016-07-07 18:25:25