Exécution de la commande shell et capture de la sortie

909

Je veux écrire une fonction qui exécutera une commande shell et retournera sa sortie sous forme de chaîne , peu importe, est-ce un message d'erreur ou de réussite. Je veux juste obtenir le même résultat que j'aurais obtenu avec la ligne de commande.

Quel serait un exemple de code qui ferait une telle chose?

Par exemple:

def run_command(cmd):
    # ??????

print run_command('mysqladmin create test -uroot -pmysqladmin12')
# Should output something like:
# mysqladmin: CREATE DATABASE failed; error: 'Can't create database 'test'; database exists'
Silver Light
la source

Réponses:

1141

La réponse à cette question dépend de la version de Python que vous utilisez. L'approche la plus simple consiste à utiliser la subprocess.check_outputfonction:

>>> subprocess.check_output(['ls', '-l'])
b'total 0\n-rw-r--r--  1 memyself  staff  0 Mar 14 11:04 files\n'

check_outputexécute un seul programme qui ne prend que des arguments en entrée. 1 Il renvoie le résultat exactement tel qu'imprimé stdout. Si vous devez écrire une entrée dans stdin, passez directement aux sections runou Popen. Si vous souhaitez exécuter des commandes shell complexes, consultez la note shell=Trueà la fin de cette réponse.

La check_outputfonction fonctionne sur presque toutes les versions de Python encore largement utilisées (2.7+). 2 Mais pour les versions plus récentes, ce n'est plus l'approche recommandée.

Versions modernes de Python (3.5 ou supérieur): run

Si vous utilisez Python 3.5 ou supérieur et n'avez pas besoin de compatibilité descendante , la nouvelle runfonction est recommandée. Il fournit une API de haut niveau très générale pour le subprocessmodule. Pour capturer la sortie d'un programme, passez l' subprocess.PIPEindicateur à l' stdoutargument mot - clé. Accédez ensuite à l' stdoutattribut de l' CompletedProcessobjet retourné :

>>> import subprocess
>>> result = subprocess.run(['ls', '-l'], stdout=subprocess.PIPE)
>>> result.stdout
b'total 0\n-rw-r--r--  1 memyself  staff  0 Mar 14 11:04 files\n'

La valeur de retour est un bytesobjet, donc si vous voulez une chaîne correcte, vous en aurez besoin decode. En supposant que le processus appelé renvoie une chaîne codée en UTF-8:

>>> result.stdout.decode('utf-8')
'total 0\n-rw-r--r--  1 memyself  staff  0 Mar 14 11:04 files\n'

Tout cela peut être compressé en une seule ligne:

>>> subprocess.run(['ls', '-l'], stdout=subprocess.PIPE).stdout.decode('utf-8')
'total 0\n-rw-r--r--  1 memyself  staff  0 Mar 14 11:04 files\n'

Si vous souhaitez transmettre une entrée aux processus stdin, passez un bytesobjet à l' inputargument mot - clé:

>>> cmd = ['awk', 'length($0) > 5']
>>> input = 'foo\nfoofoo\n'.encode('utf-8')
>>> result = subprocess.run(cmd, stdout=subprocess.PIPE, input=input)
>>> result.stdout.decode('utf-8')
'foofoo\n'

Vous pouvez capturer des erreurs en passant stderr=subprocess.PIPE(capture à result.stderr) ou stderr=subprocess.STDOUT(capture à result.stdoutavec sortie régulière). Lorsque la sécurité n'est pas un problème, vous pouvez également exécuter des commandes shell plus complexes en passant shell=Truecomme décrit dans les notes ci-dessous.

Cela ajoute juste un peu de complexité par rapport à l'ancienne façon de faire les choses. Mais je pense que ça vaut le coup: maintenant vous pouvez faire presque tout ce que vous devez faire avec la runfonction seule.

Versions plus anciennes de Python (2.7-3.4): check_output

Si vous utilisez une ancienne version de Python ou si vous avez besoin d'une rétrocompatibilité modeste, vous pouvez probablement utiliser la check_outputfonction décrite brièvement ci-dessus. Il est disponible depuis Python 2.7.

subprocess.check_output(*popenargs, **kwargs)  

Il prend les mêmes arguments que Popen(voir ci-dessous) et renvoie une chaîne contenant la sortie du programme. Le début de cette réponse contient un exemple d'utilisation plus détaillé. En Python 3.5 et supérieur, check_outputéquivaut à exécuter runavec check=Trueet stdout=PIPEet à ne renvoyer que l' stdoutattribut.

Vous pouvez passer stderr=subprocess.STDOUTpour vous assurer que les messages d'erreur sont inclus dans la sortie renvoyée - mais dans certaines versions de Python, le passage stderr=subprocess.PIPEà check_outputpeut provoquer des blocages . Lorsque la sécurité n'est pas un problème, vous pouvez également exécuter des commandes shell plus complexes en passant shell=Truecomme décrit dans les notes ci-dessous.

Si vous avez besoin de diriger stderrou de transmettre des données au processus, vous check_outputne serez pas à la hauteur de la tâche. Voir les Popenexemples ci-dessous dans ce cas.

Applications complexes et versions héritées de Python (2.6 et inférieures): Popen

Si vous avez besoin d'une compatibilité ascendante profonde ou si vous avez besoin de fonctionnalités plus sophistiquées que celles check_outputfournies, vous devrez travailler directement avec des Popenobjets, qui encapsulent l'API de bas niveau pour les sous-processus.

Le Popenconstructeur accepte soit une seule commande sans arguments, soit une liste contenant une commande comme premier élément, suivie d'un nombre quelconque d'arguments, chacun comme un élément distinct dans la liste. shlex.splitpeut aider à analyser les chaînes dans des listes correctement formatées. PopenLes objets acceptent également une multitude d'arguments différents pour la gestion des E / S de processus et la configuration de bas niveau.

Pour envoyer des entrées et capturer des sorties, communicatec'est presque toujours la méthode préférée. Un péché:

output = subprocess.Popen(["mycmd", "myarg"], 
                          stdout=subprocess.PIPE).communicate()[0]

Ou

>>> import subprocess
>>> p = subprocess.Popen(['ls', '-a'], stdout=subprocess.PIPE, 
...                                    stderr=subprocess.PIPE)
>>> out, err = p.communicate()
>>> print out
.
..
foo

Si vous définissez stdin=PIPE, communicatevous permet également de transmettre des données au processus via stdin:

>>> cmd = ['awk', 'length($0) > 5']
>>> p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
...                           stderr=subprocess.PIPE,
...                           stdin=subprocess.PIPE)
>>> out, err = p.communicate('foo\nfoofoo\n')
>>> print out
foofoo

Note Aaron réponse de Hall , qui indique que sur certains systèmes, vous devrez peut - être ensemble stdout, stderret stdintous PIPE(ou DEVNULL) pour obtenir communicateau travail du tout.

Dans certains cas rares, vous pouvez avoir besoin d'une capture de sortie complexe en temps réel. La réponse de Vartec suggère une voie à suivre, mais des méthodes autres que celles communicatesujettes à des blocages ne sont pas utilisées avec précaution.

Comme avec toutes les fonctions ci-dessus, lorsque la sécurité n'est pas un problème, vous pouvez exécuter des commandes shell plus complexes en passant shell=True.

Remarques

1. Exécuter des commandes shell: l' shell=Trueargument

Normalement, chaque appel à run, check_outputou le Popenconstructeur exécute un seul programme . Cela signifie pas de tuyaux de style bash fantaisie. Si vous souhaitez exécuter des commandes shell complexes, vous pouvez passer shell=True, que les trois fonctions prennent en charge.

Cependant, cela pose des problèmes de sécurité . Si vous faites autre chose que des scripts légers, vous feriez mieux d'appeler chaque processus séparément et de passer la sortie de chacun comme entrée au suivant, via

run(cmd, [stdout=etc...], input=other_output)

Ou

Popen(cmd, [stdout=etc...]).communicate(other_output)

La tentation de connecter directement les tuyaux est forte; Résiste. Sinon, vous verrez probablement des blocages ou devrez faire des choses comme ça .

2. Considérations Unicode

check_outputrenvoie une chaîne en Python 2, mais un bytesobjet en Python 3. Cela vaut la peine de prendre un moment pour en apprendre unicode si vous ne l'avez pas déjà fait.

senderle
la source
5
Avec check_output()et communicate()vous devez attendre que le processus soit terminé, poll()vous obtenez la sortie au fur et à mesure. Cela dépend vraiment de ce dont vous avez besoin.
vartec
2
Je ne sais pas si cela ne s'applique qu'aux versions ultérieures de Python, mais la variable outétait de type <class 'bytes'>pour moi. Afin d'obtenir la sortie sous forme de chaîne, j'ai dû la décoder avant d'imprimer comme out.decode("utf-8")
suit
1
@par Ça ne marche pas pour toi quand tu passes shell=True? Ça marche pour moi. Vous n'avez pas besoin de shlex.splitpasser shell=True. shlex.splitest pour les commandes non shell. Je pense que je vais retirer ce morceau parce que cela embrouille les eaux.
senderle
2
Python 3.5+ permet un argument mot-clé universal_newlines=Truequi vous permet de passer et de retirer des chaînes Unicode dans l'encodage par défaut du système. En 3.7, cela a été renommé le plus sensible text=True.
tripleee
2
Pour Python 3.6+, vous pouvez utiliser le encodingparamètre de subprocess.runau lieu d'utiliser result.stdout.decode('utf-8'), vous pouvez utiliser subprocess.run(['ls', '-l'], stdout=subprocess.PIPE, encoding='utf-8').
Pierre
191

C'est beaucoup plus facile, mais ne fonctionne que sur Unix (y compris Cygwin) et Python2.7.

import commands
print commands.getstatusoutput('wc -l file')

Il retourne un tuple avec (return_value, output).

Pour une solution qui fonctionne à la fois en Python2 et Python3, utilisez subprocessplutôt le module:

from subprocess import Popen, PIPE
output = Popen(["date"],stdout=PIPE)
response = output.communicate()
print response
byte_array
la source
31
Déconseillé maintenant, mais très utile pour les anciennes versions de python sans
subprocess.check_output
22
Notez que ceci est spécifique à Unix. Il échouera par exemple sous Windows.
Zitrax
4
+1 Je dois travailler sur une ancienne version de python 2.4 et cela a été TRÈS utile
javadba
1
Quel est le type de PIPE, montrez le code complet: subprocess.PIPE
Kyle Bridenstine
@KyleBridenstine, vous pouvez modifier les réponses.
Boris
106

Quelque chose comme ca:

def runProcess(exe):    
    p = subprocess.Popen(exe, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    while(True):
        # returns None while subprocess is running
        retcode = p.poll() 
        line = p.stdout.readline()
        yield line
        if retcode is not None:
            break

Notez que je redirige stderr vers stdout, ce n'est peut-être pas exactement ce que vous voulez, mais je veux aussi des messages d'erreur.

Cette fonction produit ligne par ligne au fur et à mesure (normalement, vous devez attendre la fin du sous-processus pour obtenir la sortie dans son ensemble).

Pour votre cas, l'utilisation serait:

for line in runProcess('mysqladmin create test -uroot -pmysqladmin12'.split()):
    print line,
vartec
la source
Assurez-vous d'implémenter une sorte de boucle active pour obtenir la sortie afin d'éviter le blocage potentiel waitet les callfonctions.
André Caron
@Silver Light: votre processus attend probablement l'entrée de l'utilisateur. Essayez de fournir une PIPEvaleur stdinet de fermer ce fichier dès qu'il Popenrevient.
André Caron
4
-1: il est une boucle infinie si l' retcodeest 0. Le chèque devrait être if retcode is not None. Vous ne devriez pas donner des chaînes vides (même une ligne vide est au moins un symbole « \ n »): if line: yield line. Appelez p.stdout.close()à la fin.
jfs
2
J'ai essayé le code avec ls -l / dirname et il se casse après avoir répertorié deux fichiers alors qu'il y a beaucoup plus de fichiers dans le répertoire
Vasilis
3
@fuenfundachtzig: .readlines()ne reviendra pas tant que toutes les sorties n'auront pas été lues et par conséquent il casse pour les sorties de grande taille qui ne tiennent pas en mémoire. De plus, pour éviter de manquer des données tamponnées après la fin du sous-processus, il devrait y avoir un analogue deif retcode is not None: yield from p.stdout.readlines(); break
jfs
67

La réponse de Vartec ne lit pas toutes les lignes, j'ai donc fait une version qui l'a fait:

def run_command(command):
    p = subprocess.Popen(command,
                         stdout=subprocess.PIPE,
                         stderr=subprocess.STDOUT)
    return iter(p.stdout.readline, b'')

L'utilisation est la même que la réponse acceptée:

command = 'mysqladmin create test -uroot -pmysqladmin12'.split()
for line in run_command(command):
    print(line)
Max Ekman
la source
6
vous pouvez utiliser à la return iter(p.stdout.readline, b'')place de la boucle while
jfs
2
C'est une utilisation assez cool d'iter, je ne savais pas ça! J'ai mis à jour le code.
Max Ekman
Je suis sûr que stdout conserve toutes les sorties, c'est un objet stream avec un tampon. J'utilise une technique très similaire pour épuiser toutes les sorties restantes après la fin d'un Popen, et dans mon cas, en utilisant poll () et readline pendant l'exécution pour capturer la sortie en direct également.
Max Ekman
J'ai supprimé mon commentaire trompeur. Je peux confirmer, p.stdout.readline()peut retourner la sortie non vide précédemment mise en mémoire tampon même si le processus enfant a déjà quitté ( p.poll()n'est pas None).
jfs
Ce code ne fonctionne pas. Voir ici stackoverflow.com/questions/24340877/…
thang
61

Il s'agit d'une solution délicate mais super simple qui fonctionne dans de nombreuses situations:

import os
os.system('sample_cmd > tmp')
print open('tmp', 'r').read()

Un fichier temporaire (ici tmp) est créé avec la sortie de la commande et vous pouvez y lire la sortie souhaitée.

Note supplémentaire dans les commentaires: vous pouvez supprimer le fichier tmp dans le cas d'un travail unique. Si vous devez le faire plusieurs fois, il n'est pas nécessaire de supprimer le tmp.

os.remove('tmp')
Mehdi Saman Booy
la source
5
Hacky mais super simple + fonctionne n'importe où .. peut le combiner avec mktemppour le faire fonctionner dans des situations filetées je suppose
Prakash Rajagaopal
2
Peut-être la méthode la plus rapide, mais il vaut mieux l'ajouter os.remove('tmp')pour la rendre "sans fichier".
XuMuK
@XuMuK Vous avez raison dans le cas d'un emploi unique. S'il s'agit d'un travail répétitif, la suppression n'est peut-être pas nécessaire
Mehdi Saman Booy
1
mauvais pour la simultanéité, mauvais pour les fonctions réentrantes, mauvais pour ne pas laisser le système tel qu'il était avant qu'il ne démarre (pas de nettoyage)
2mia
1
@ 2mia Évidemment, c'est facile pour une raison! Si vous souhaitez utiliser le fichier comme une sorte de mémoire partagée pour des lectures et des écritures simultanées, ce n'est pas un bon choix. Mais, pour s.th. comme avoir la sortie d'une commande (par exemple ls ou find ou ...), cela peut être un bon choix et rapide. Btw si vous avez besoin d'une solution rapide pour un problème simple, c'est le meilleur je pense. Si vous avez besoin d'un pipeline, le sous-processus fonctionne pour vous plus efficacement.
Mehdi Saman Booy
44

J'ai eu le même problème mais j'ai trouvé un moyen très simple de le faire:

import subprocess
output = subprocess.getoutput("ls -l")
print(output)

J'espère que ça aide

Remarque: Cette solution est spécifique à Python3 car subprocess.getoutput()elle ne fonctionne pas dans Python2

azhar22k
la source
Comment cela résout-il le problème du PO? Veuillez développer.
RamenChef
4
Il renvoie la sortie de la commande sous forme de chaîne, aussi simple que cela
azhar22k
1
Bien sûr, print est une déclaration sur Python 2. Vous devriez pouvoir comprendre qu'il s'agit d'une réponse Python 3.
2
@Dev print (s) est valide en python 2. subprocess.getoutput ne l'est pas.
user48956
2
Pour la plupart des cas d'utilisation, c'est ce que les gens voudront probablement: facile à retenir, pas besoin de décoder les résultats, etc. Merci.
bwv549
18

Vous pouvez utiliser les commandes suivantes pour exécuter n'importe quelle commande shell. Je les ai utilisés sur Ubuntu.

import os
os.popen('your command here').read()

Remarque: Ceci est obsolète depuis python 2.6. Vous devez maintenant utiliser subprocess.Popen. Voici l'exemple

import subprocess

p = subprocess.Popen("Your command", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()[0]
print p.split("\n")
Muhammad Hassan
la source
2
Déconseillé depuis la version 2.6 - docs.python.org/2/library/os.html#os.popen
Filippo Vitale
1
@FilippoVitale Merci. Je ne savais pas qu'il était obsolète.
Muhammad Hassan
1
Selon raspberrypi.stackexchange.com/questions/71547/… os.popen() est déconseillé dans Python 2.6, mais il n'est pas déconseillé dans Python 3.x, car dans 3.x il est implémenté en utilisant subprocess.Popen().
JL
12

Votre kilométrage peut varier, j'ai tenté de faire tourner @ senderle sur la solution de Vartec dans Windows sur Python 2.6.5, mais je recevais des erreurs et aucune autre solution n'a fonctionné. Mon erreur était: WindowsError: [Error 6] The handle is invalid.

J'ai trouvé que je devais assigner PIPE à chaque poignée pour qu'elle renvoie la sortie que j'attendais - ce qui suit a fonctionné pour moi.

import subprocess

def run_command(cmd):
    """given shell command, returns communication tuple of stdout and stderr"""
    return subprocess.Popen(cmd, 
                            stdout=subprocess.PIPE, 
                            stderr=subprocess.PIPE, 
                            stdin=subprocess.PIPE).communicate()

et appelez comme ceci, ( [0]obtient le premier élément du tuple, stdout):

run_command('tracert 11.1.0.1')[0]

Après avoir appris plus, je crois que j'ai besoin de ces arguments de pipe parce que je travaille sur un système personnalisé qui utilise différentes poignées, j'ai donc dû contrôler directement tous les std.

Pour arrêter les fenêtres contextuelles de la console (avec Windows), procédez comme suit:

def run_command(cmd):
    """given shell command, returns communication tuple of stdout and stderr"""
    # instantiate a startupinfo obj:
    startupinfo = subprocess.STARTUPINFO()
    # set the use show window flag, might make conditional on being in Windows:
    startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
    # pass as the startupinfo keyword argument:
    return subprocess.Popen(cmd,
                            stdout=subprocess.PIPE, 
                            stderr=subprocess.PIPE, 
                            stdin=subprocess.PIPE, 
                            startupinfo=startupinfo).communicate()

run_command('tracert 11.1.0.1')
Aaron Hall
la source
1
Intéressant - cela doit être une chose Windows. J'ajouterai une note à ce sujet au cas où les gens obtiendraient des erreurs similaires.
senderle
utilisez DEVNULLau lieu de subprocess.PIPEsi vous n'écrivez / ne lisez pas à partir d'un canal, sinon vous risquez de bloquer le processus enfant.
jfs
10

J'avais une saveur légèrement différente du même problème avec les exigences suivantes:

  1. Capturez et renvoyez les messages STDOUT à mesure qu'ils s'accumulent dans le tampon STDOUT (c'est-à-dire en temps réel).
    • @vartec a résolu ce problème en Python avec son utilisation de générateurs et le
      mot-clé 'yield' ci-dessus
  2. Imprimer toutes les lignes STDOUT ( même si le processus se termine avant que le tampon STDOUT puisse être entièrement lu )
  3. Ne perdez pas de cycles CPU à interroger le processus à haute fréquence
  4. Vérifiez le code retour du sous-processus
  5. Imprimez STDERR (distinct de STDOUT) si nous obtenons un code retour d'erreur non nul.

J'ai combiné et peaufiné les réponses précédentes pour arriver aux éléments suivants:

import subprocess
from time import sleep

def run_command(command):
    p = subprocess.Popen(command,
                         stdout=subprocess.PIPE,
                         stderr=subprocess.PIPE,
                         shell=True)
    # Read stdout from subprocess until the buffer is empty !
    for line in iter(p.stdout.readline, b''):
        if line: # Don't print blank lines
            yield line
    # This ensures the process has completed, AND sets the 'returncode' attr
    while p.poll() is None:                                                                                                                                        
        sleep(.1) #Don't waste CPU-cycles
    # Empty STDERR buffer
    err = p.stderr.read()
    if p.returncode != 0:
       # The run_command() function is responsible for logging STDERR 
       print("Error: " + str(err))

Ce code serait exécuté de la même manière que les réponses précédentes:

for line in run_command(cmd):
    print(line)
L'Aelfinn
la source
1
Cela vous dérange-t-il d'expliquer comment l'ajout de sommeil (.1) ne gaspillera pas les cycles du processeur?
Moataz Elmasry
2
Si nous continuions à appeler p.poll()sans sommeil entre les appels, nous gaspillerions les cycles CPU en appelant cette fonction des millions de fois. Au lieu de cela, nous "étranglons" notre boucle en disant au système d'exploitation que nous n'avons pas besoin d'être dérangés pour le 1 / 10ème de seconde suivant, afin qu'il puisse effectuer d'autres tâches. (Il est possible que p.poll () dorme également, ce qui rend notre déclaration de sommeil redondante).
The Aelfinn
5

La division de la commande initiale pour le subprocesspeut être délicate et lourde.

Utilisez shlex.split()pour vous aider.

Exemple de commande

git log -n 5 --since "5 years ago" --until "2 year ago"

Le code

from subprocess import check_output
from shlex import split

res = check_output(split('git log -n 5 --since "5 years ago" --until "2 year ago"'))
print(res)
>>> b'commit 7696ab087a163e084d6870bb4e5e4d4198bdc61a\nAuthor: Artur Barseghyan...'

Sans shlex.split()le code se présenterait comme suit

res = check_output([
    'git', 
    'log', 
    '-n', 
    '5', 
    '--since', 
    '5 years ago', 
    '--until', 
    '2 year ago'
])
print(res)
>>> b'commit 7696ab087a163e084d6870bb4e5e4d4198bdc61a\nAuthor: Artur Barseghyan...'
Artur Barseghyan
la source
1
shlex.split()est pratique, surtout si vous ne savez pas exactement comment fonctionne la citation dans le shell; mais la conversion manuelle de cette chaîne dans la liste ['git', 'log', '-n', '5', '--since', '5 years ago', '--until', '2 year ago']n'est pas difficile du tout si vous comprenez la citation.
tripleee
4

Si vous devez exécuter une commande shell sur plusieurs fichiers, cela a fait l'affaire pour moi.

import os
import subprocess

# Define a function for running commands and capturing stdout line by line
# (Modified from Vartec's solution because it wasn't printing all lines)
def runProcess(exe):    
    p = subprocess.Popen(exe, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    return iter(p.stdout.readline, b'')

# Get all filenames in working directory
for filename in os.listdir('./'):
    # This command will be run on each file
    cmd = 'nm ' + filename

    # Run the command and capture the output line by line.
    for line in runProcess(cmd.split()):
        # Eliminate leading and trailing whitespace
        line.strip()
        # Split the output 
        output = line.split()

        # Filter the output and print relevant lines
        if len(output) > 2:
            if ((output[2] == 'set_program_name')):
                print filename
                print line

Edit: Je viens de voir la solution de Max Persson avec la suggestion de JF Sebastian. Avancé et incorporé cela.

Ethan Strider
la source
Popenaccepte soit une chaîne, mais alors vous avez besoin shell=True, ou une liste d'arguments, auquel cas vous devez passer à la ['nm', filename]place d'une chaîne. Ce dernier est préférable car le shell ajoute de la complexité sans apporter de valeur ici. Passer une chaîne sans shell=Trueapparemment fonctionne sur Windows, mais cela pourrait changer dans n'importe quelle prochaine version de Python.
tripleee
2

Selon @senderle, si vous utilisez python3.6 comme moi:

def sh(cmd, input=""):
    rst = subprocess.run(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, input=input.encode("utf-8"))
    assert rst.returncode == 0, rst.stderr.decode("utf-8")
    return rst.stdout.decode("utf-8")
sh("ls -a")

Agira exactement comme si vous exécutiez la commande dans bash

Neo li
la source
Vous réinventez les arguments des mots clés check=True, universal_newlines=True. En d'autres termes, fait subprocess.run()déjà tout ce que fait votre code.
tripleee
1

Si vous utilisez le subprocessmodule python, vous pouvez gérer le STDOUT, le STDERR et renvoyer le code de commande séparément. Vous pouvez voir un exemple pour l'implémentation complète de l'appelant de commande. Bien sûr, vous pouvez l'étendre try..exceptsi vous le souhaitez.

La fonction ci-dessous renvoie le code STDOUT, STDERR et Return afin que vous puissiez les gérer dans l'autre script.

import subprocess

def command_caller(command=None)
    sp = subprocess.Popen(command, stderr=subprocess.PIPE, stdout=subprocess.PIPE, shell=False)
    out, err = sp.communicate()
    if sp.returncode:
        print(
            "Return code: %(ret_code)s Error message: %(err_msg)s"
            % {"ret_code": sp.returncode, "err_msg": err}
            )
    return sp.returncode, out, err
milanbalazs
la source
Une autre mauvaise réimplémentation de subprocess.run(). Ne réinventez pas la roue.
tripleee
0

par exemple, exécuter ('ls -ahl') différencié trois / quatre retours possibles et plateformes OS:

  1. aucune sortie, mais exécuté avec succès
  2. sortie ligne vide, exécution réussie
  3. échec de l'exécution
  4. sortir quelque chose, exécuter avec succès

fonction ci-dessous

def execute(cmd, output=True, DEBUG_MODE=False):
"""Executes a bash command.
(cmd, output=True)
output: whether print shell output to screen, only affects screen display, does not affect returned values
return: ...regardless of output=True/False...
        returns shell output as a list with each elment is a line of string (whitespace stripped both sides) from output
        could be 
        [], ie, len()=0 --> no output;    
        [''] --> output empty line;     
        None --> error occured, see below

        if error ocurs, returns None (ie, is None), print out the error message to screen
"""
if not DEBUG_MODE:
    print "Command: " + cmd

    # https://stackoverflow.com/a/40139101/2292993
    def _execute_cmd(cmd):
        if os.name == 'nt' or platform.system() == 'Windows':
            # set stdin, out, err all to PIPE to get results (other than None) after run the Popen() instance
            p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
        else:
            # Use bash; the default is sh
            p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, executable="/bin/bash")

        # the Popen() instance starts running once instantiated (??)
        # additionally, communicate(), or poll() and wait process to terminate
        # communicate() accepts optional input as stdin to the pipe (requires setting stdin=subprocess.PIPE above), return out, err as tuple
        # if communicate(), the results are buffered in memory

        # Read stdout from subprocess until the buffer is empty !
        # if error occurs, the stdout is '', which means the below loop is essentially skipped
        # A prefix of 'b' or 'B' is ignored in Python 2; 
        # it indicates that the literal should become a bytes literal in Python 3 
        # (e.g. when code is automatically converted with 2to3).
        # return iter(p.stdout.readline, b'')
        for line in iter(p.stdout.readline, b''):
            # # Windows has \r\n, Unix has \n, Old mac has \r
            # if line not in ['','\n','\r','\r\n']: # Don't print blank lines
                yield line
        while p.poll() is None:                                                                                                                                        
            sleep(.1) #Don't waste CPU-cycles
        # Empty STDERR buffer
        err = p.stderr.read()
        if p.returncode != 0:
            # responsible for logging STDERR 
            print("Error: " + str(err))
            yield None

    out = []
    for line in _execute_cmd(cmd):
        # error did not occur earlier
        if line is not None:
            # trailing comma to avoid a newline (by print itself) being printed
            if output: print line,
            out.append(line.strip())
        else:
            # error occured earlier
            out = None
    return out
else:
    print "Simulation! The command is " + cmd
    print ""
Jerry T
la source
0

La sortie peut être redirigée vers un fichier texte, puis relue.

import subprocess
import os
import tempfile

def execute_to_file(command):
    """
    This function execute the command
    and pass its output to a tempfile then read it back
    It is usefull for process that deploy child process
    """
    temp_file = tempfile.NamedTemporaryFile(delete=False)
    temp_file.close()
    path = temp_file.name
    command = command + " > " + path
    proc = subprocess.run(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
    if proc.stderr:
        # if command failed return
        os.unlink(path)
        return
    with open(path, 'r') as f:
        data = f.read()
    os.unlink(path)
    return data

if __name__ == "__main__":
    path = "Somepath"
    command = 'ecls.exe /files ' + path
    print(execute(command))
MONSIEUR
la source
Bien sûr , mais pourquoi voudriez-vous; et pourquoi utiliseriez-vous le shell au lieu de passer stdout=temp_file?
tripleee
En fait, de manière générale, vous avez raison, mais dans mon exemple, le ecls.exesemble déployer un autre outil de ligne de commande, donc la méthode simple ne fonctionnait pas parfois.
MR
0

vient d'écrire un petit script bash pour ce faire en utilisant curl

https://gist.github.com/harish2704/bfb8abece94893c53ce344548ead8ba5

#!/usr/bin/env bash

# Usage: gdrive_dl.sh <url>

urlBase='https://drive.google.com'
fCookie=tmpcookies

curl="curl -L -b $fCookie -c $fCookie"
confirm(){
    $curl "$1" | grep jfk-button-action | sed -e 's/.*jfk-button-action" href="\(\S*\)".*/\1/' -e 's/\&amp;/\&/g'
}

$curl -O -J "${urlBase}$(confirm $1)"
harish2704
la source