[PYTHON] Comportement en donnant une liste avec shell = True dans le sous-processus

Origine

Si vous essayez d'exécuter un fichier avec jar intégré dans un script shell comme embulk ou digdag en utilisant un sous-processus Python avec shell = False

>>> import subprocess
>>> subprocess.check_call(["digdag"])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/Cellar/python/2.7.13/Frameworks/Python.framework/Versions/2.7/lib/python2.7/subprocess.py", line 181, in check_call
    retcode = call(*popenargs, **kwargs)
  File "/usr/local/Cellar/python/2.7.13/Frameworks/Python.framework/Versions/2.7/lib/python2.7/subprocess.py", line 168, in call
    return Popen(*popenargs, **kwargs).wait()
  File "/usr/local/Cellar/python/2.7.13/Frameworks/Python.framework/Versions/2.7/lib/python2.7/subprocess.py", line 390, in __init__
    errread, errwrite)
  File "/usr/local/Cellar/python/2.7.13/Frameworks/Python.framework/Versions/2.7/lib/python2.7/subprocess.py", line 1024, in _execute_child
    raise child_exception
OSError: [Errno 8] Exec format error

Cela échoue comme. Cela peut être évité en définissant shell = True, mais j'ai étudié comment passer des arguments au programme car c'était différent lorsque shell était False (par défaut) et quand il était True.

Ce que je me demandais

Si vous donnez une liste lorsque shell = False, elle sera traitée comme une commande et ses arguments. Par exemple

sample.sh


#!/bin/sh
echo "$1", "$2", "$3" > out.txt

Code qui appelle le script shell appelé à l'aide de subprocess.check_call

caller_l0.py


import subprocess
cmd = ["./sample.sh", "1starg", "2ndarg", "3rdarg"]
subprocess.check_call(cmd, shell=False)

Lorsque vous exécutez

$ python caller_l0.py
$ cat out.txt
1starg, 2ndarg, 3rdarg

Vous pouvez voir que les arguments sont passés comme prévu. Réglez-le simplement sur shell = True et

caller_l1.py


import subprocess
cmd = ["./sample.sh", "1starg", "2ndarg", "3rdarg"]
subprocess.check_call(cmd, shell=True)

Lorsque vous exécutez

$ python caller_l1.py
$ cat out.txt
, ,

Où êtes-vous allé après le deuxième élément de la liste?

Ce que j'ai regardé

La documentation officielle (https://docs.python.org/3/library/subprocess.html) décrit les paramètres args à passer au sous-processus:

args is required for all calls and should be a string, or a sequence of program arguments.
Providing a sequence of arguments is generally preferred, as it allows the module to take
care of any required escaping and quoting of arguments (e.g. to permit spaces in file names).
If passing a single string, either shell must be True (see below) or else the string must
simply name the program to be executed without specifying any arguments.

Il n'y a pas d'explication ici pour le cas où la liste est donnée avec shell = True. Si tu regardes un peu plus

If args is a sequence, the first item specifies the command string, and any additional
items will be treated as additional arguments to the shell itself.
That is to say, Popen does the equivalent of:

  Popen(['/bin/sh', '-c', args[0], args[1], ...])

Sur MacOS, / bin / sh est bash, donc si vous faites man

-c string If  the  -c  option  is  present, then commands are read from
          string.  If there are arguments after the  string,  they  are
          assigned to the positional parameters, starting with $0.

Je ne l'ai pas compris, alors je l'ai cherché https://stackoverflow.com/questions/1711970/cant-seem-to-use-bash-c-option-with-arguments-after-the-c Je suis arrivé à -option-string et j'ai compris que \ $ 0, \ $ 1, ... dans la chaîne donnée à -c serait remplacé.

caller_l1_.py


import subprocess
cmd = ["./sample.sh $0 $1 $2", "1starg", "2ndarg", "3rdarg"]
subprocess.check_call(cmd, shell=True)
$ python caller_l1_.py
$ cat out.txt
1starg, 2ndarg, 3rdarg

Arguments après sh -c

Quand j'ai vérifié le comportement lorsque plusieurs arguments étaient passés après sh -c, autres que bash, c'était le même comportement avec le tiret d'Ubuntu et sh de FreeBSD. De là, je me suis demandé s'il y avait une utilité pour cela.

$ bash -c '$0 $1, $2, $3' echo '1-0 1-1' 2 3
1-0 1-1, 2, 3

Ensuite, appliquez celui cité et faites de même via le sous-processus.

$ python -c 'import subprocess;subprocess.check_call(["$0 $1, $2, $3", "echo", "1-0 1-1", "2", "3"], shell=True)'
1-0 1-1, 2, 3

Cependant, si tel est le cas, vous pouvez étendre la chaîne de caractères depuis le début, tandis que d'un autre côté

caller_l1__.py


import subprocess
cmd = ["$0 $1 $2 $3", "./sample.sh", "1-1 1-2", "2", "3"]
subprocess.check_call(cmd, shell=True)

Parce que celui qui a été remplacé n'est passé qu'à sample.sh

$ python caller_l1__.py
$ cat out.txt
1-1, 1-2, 2

Après tout, je ne savais pas trop où donner plusieurs arguments après -c.

Résumé

Le comportement lorsqu'une liste est passée avec shell = True dans le sous-processus Python est similaire au comportement lorsque plusieurs arguments sont passés après sh -c. Cependant, je ne savais pas comment l'utiliser (quelqu'un me l'a dit).

Lorsque shell = True, j'ai senti qu'il était simple de spécifier une chaîne de caractères au lieu d'une liste.

caller_s1.py


import subprocess
cmd = "./sample.sh 1starg 2ndarg 3rdarg"
subprocess.check_call(cmd, shell=True)
$ python2.7 caller_s1.py
$ cat out.txt
1starg, 2ndarg, 3rdarg

Recommended Posts

Comportement en donnant une liste avec shell = True dans le sous-processus
Lors de la création d'une matrice dans une liste
Comportement lors du retour dans le bloc with
Choses à noter lors de l'initialisation d'une liste en Python
Utilisez communiquer () lors de la réception de la sortie dans un sous-processus Python
Différences de comportement entre les opérateurs append () et "+ =" lors de l'ajout de données à une liste en Python
Créez un indicateur dans les paramètres qui seront True uniquement lors du test avec Django
Essayez de programmer avec un shell!
Comportement dans chaque langue lorsque les collouts sont réutilisés avec for
Traitez les fichiers du dossier dans l'ordre avec le script shell
Exécutez une commande dans Go comme subprocess.call de Python (~, shell = True)
Flask-Créer une liste Todo avec des mesures CSRF avec WTF avec Flask
Traitez le contenu du fichier dans l'ordre avec un script shell
Obtenez une liste de fichiers dans un dossier avec python sans chemin
recharger dans le shell django avec ipython
[python] Gérer les fonctions dans une liste
Lors de l'écriture d'un programme en Python
Obtenez une liste des packages installés dans l'environnement actuel avec python
Générez une liste contenant le nombre de jours du mois en cours.
Recevez une liste des résultats du traitement parallèle en Python avec starmap
Que faire lorsqu'un message d'avertissement est affiché dans la liste des pip
Livre en spirale en Python! Python avec un livre en spirale! (Chapitre 14 ~)
Python3> dans le mot clé> Vrai avec une correspondance partielle?
Précautions lors du décapage d'une fonction en python
Afficher une liste d'alphabets en Python 3
Dessinez un cœur en rubis avec PyCall
Changer la liste dans l'instruction for
Une solution de contournement lors de l'installation de pyAudio avec pip.
Gérez les générations de rotation des journaux avec un script shell.
Implémenter un modèle avec état et comportement
Comment passer le résultat de l'exécution d'une commande shell dans une liste en Python
Différences de comportement de chaque langage LL lorsque l'index de la liste est ignoré
Si vous obtenez une longue erreur lors de la tabulation d'un shell interactif avec Anaconda
Les chaînes de caractères placées dans GCS avec python sont déformées lorsqu'elles sont visualisées avec un navigateur
Comment obtenir une liste de fichiers dans le même répertoire avec python