Comment exécuter un script Python à partir de C #?

131

Ce genre de question a déjà été posé à des degrés divers, mais j'estime qu'il n'y a pas eu de réponse concise et je la pose donc à nouveau.

Je souhaite exécuter un script en Python. Disons que c'est ceci:

if __name__ == '__main__':
    with open(sys.argv[1], 'r') as f:
        s = f.read()
    print s

Qui obtient un emplacement de fichier, le lit, puis imprime son contenu. Pas si compliqué.

D'accord, alors comment puis-je l'exécuter en C #?

Voici ce que j'ai maintenant:

    private void run_cmd(string cmd, string args)
    {
        ProcessStartInfo start = new ProcessStartInfo();
        start.FileName = cmd;
        start.Arguments = args;
        start.UseShellExecute = false;
        start.RedirectStandardOutput = true;
        using (Process process = Process.Start(start))
        {
            using (StreamReader reader = process.StandardOutput)
            {
                string result = reader.ReadToEnd();
                Console.Write(result);
            }
        }
    }

Quand je passe l' code.pyemplacement au fur cmdet à mesure et l' filenameemplacement car argscela ne fonctionne pas. On m'a dit que je devrais passer python.exepour le cmd, puis code.py filenamecomme le args.

Je cherche depuis un moment maintenant et je ne peux trouver que des personnes suggérant d'utiliser IronPython ou autre. Mais il doit y avoir un moyen d'appeler un script Python à partir de C #.

Quelques précisions:

Je dois l'exécuter à partir de C #, je dois capturer la sortie et je ne peux pas utiliser IronPython ou quoi que ce soit d'autre. Quel que soit le hack que vous avez, ce sera bien.

PS: Le code Python que j'exécute est beaucoup plus complexe que cela, et il renvoie la sortie dont j'ai besoin en C #, et le code C # appellera constamment le code Python.

Prétendez que c'est mon code:

    private void get_vals()
    {
        for (int i = 0; i < 100; i++)
        {
            run_cmd("code.py", i);
        }
    }
Rose Inbar
la source
1
Le code C # doit-il appeler le script Python ou est-ce OK si (comme vous l'avez déclaré à la fin) vous appelez simplement l'interpréteur python qui exécute ensuite le script?
Gerald Versluis
1
Êtes-vous autorisé à utiliser IronPython?
Eugene
1
@GeraldVersluis ça va, j'ai juste besoin de pouvoir l'exécuter via c # et d'attraper la sortie.
Inbar Rose

Réponses:

124

La raison pour laquelle cela ne fonctionne pas est que vous avez UseShellExecute = false.

Si vous n'utilisez pas le shell, vous devrez fournir le chemin complet de l'exécutable python en tant que FileName, et créer la Argumentschaîne pour fournir à la fois votre script et le fichier que vous souhaitez lire.

Notez également que vous ne pouvez pas à RedirectStandardOutputmoins UseShellExecute = false.

Je ne sais pas trop comment la chaîne d'argument doit être formatée pour python, mais vous aurez besoin de quelque chose comme ceci:

private void run_cmd(string cmd, string args)
{
     ProcessStartInfo start = new ProcessStartInfo();
     start.FileName = "my/full/path/to/python.exe";
     start.Arguments = string.Format("{0} {1}", cmd, args);
     start.UseShellExecute = false;
     start.RedirectStandardOutput = true;
     using(Process process = Process.Start(start))
     {
         using(StreamReader reader = process.StandardOutput)
         {
             string result = reader.ReadToEnd();
             Console.Write(result);
         }
     }
}
Maître Moralité
la source
je veux être en mesure de prendre la sortie de mon programme pour être utilisée plus tard. donc j'ai besoin d'avoir shellexecute comme faux. vous dites si je passe au c:\python26\python.exefur cmdet à c:\temp\code.py c:\temp\testfile.txtmesure que argscela devrait fonctionner?
Inbar Rose
J'ai mis à jour avec un exemple rapide, j'ai rencontré le même problème lorsque j'ai fait quelque chose de similaire avec node
Master Morality
ok, ça marche pour moi maintenant. le problème est que vous devez formater les chaînes très soigneusement. tous les chemins ont besoin "PATH"même s'il n'y a pas d'espaces ... étrange ...
Inbar Rose
Le mien fonctionne sans fournir le chemin complet. Je mets UseShellExecuteà falseet RedirectStandardOutputà true.
Nikhil Girraj
1
Fonctionne sans fournir le chemin si le programme d'installation Python a été invité à ajouter le répertoire Python à la variable d'environnement PATH (spécifique au système ou à l'utilisateur).
Manfred
59

Si vous souhaitez utiliser IronPython, vous pouvez exécuter des scripts directement en C #:

using IronPython.Hosting;
using Microsoft.Scripting.Hosting;

private static void doPython()
{
    ScriptEngine engine = Python.CreateEngine();
    engine.ExecuteFile(@"test.py");
}

Obtenez IronPython ici.

Chris Dunaway
la source
3
Expliquez-moi cela, puis-je le faire sans rien télécharger? c # est-il prêt avec ce plugin? aussi - puis-je exécuter des scripts externes avec cela?
Inbar Rose
Vous devrez installer IronPython, qui est open source. J'ai mis à jour la réponse.
Chris Dunaway
7
notez qu'IronPython et Python ne sont pas exactement la même chose. Juste pour mémoire ...
Ron Klein
1
IronPython ne semble pas prendre en charge le même ensemble de fonctionnalités de langage que Python 3.6.x. Par conséquent, IronPython n'est une option que si vous n'utilisez aucune de ces fonctionnalités de langage. Cela vaut vraiment la peine d'essayer.
Manfred
1
@RonKlein, ce ne sont pas les mêmes, et si vous essayez d'utiliser une bibliothèque telle que scikit-learn (à des fins de ML), cela ne fonctionnera pas non plus pour vous
moarra
28

Exécuter le script Python à partir de C

Créez un projet C # et écrivez le code suivant.

using System;
using System.Diagnostics;
using System.IO;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            run_cmd();
        }

        private void run_cmd()
        {

            string fileName = @"C:\sample_script.py";

            Process p = new Process();
            p.StartInfo = new ProcessStartInfo(@"C:\Python27\python.exe", fileName)
            {
                RedirectStandardOutput = true,
                UseShellExecute = false,
                CreateNoWindow = true
            };
            p.Start();

            string output = p.StandardOutput.ReadToEnd();
            p.WaitForExit();

            Console.WriteLine(output);

            Console.ReadLine();

        }
    }
}

Python sample_script

print "Test Python C #"

Vous verrez le 'Python C # Test' dans la console de C #.

Rohit Salunke
la source
J'ai un fichier de script python dans un dossier Windows comme C:\Users\Documents\Visual Studio 2019. En raison de l'espace dans le chemin du dossier, il est traité comme un délimiteur d'arguments, donc j'ai obtenu le fileNamene peut pas être trouvé. Existe-t-il un moyen d'échapper à l'espace?
Leon Chang
15

J'ai rencontré le même problème et la réponse de Maître Moralité ne l'a pas fait pour moi. Ce qui suit, basé sur la réponse précédente, a fonctionné:

private void run_cmd(string cmd, string args)
{
 ProcessStartInfo start = new ProcessStartInfo();
 start.FileName = cmd;//cmd is full path to python.exe
 start.Arguments = args;//args is path to .py file and any cmd line args
 start.UseShellExecute = false;
 start.RedirectStandardOutput = true;
 using(Process process = Process.Start(start))
 {
     using(StreamReader reader = process.StandardOutput)
     {
         string result = reader.ReadToEnd();
         Console.Write(result);
     }
 }
}

Par exemple, cmd serait @C:/Python26/python.exeet args serait C://Python26//test.py 100si vous vouliez exécuter test.py avec l'argument de ligne cmd 100. Notez que le chemin du fichier .py n'a pas le symbole @.

Derek
la source
2
Quelqu'un peut-il expliquer pourquoi c'est -1. Qu'est ce qui ne va pas avec ça.
Keith Loughnane
2
C'est exactement la même chose que la réponse ci-dessus, simplement votre attribution de FileName à partir d'un paramètre à la place. Mais à la fin starta les mêmes valeurs
nid
4

Juste aussi pour attirer votre attention sur ceci:

https://code.msdn.microsoft.com/windowsdesktop/C-and-Python-interprocess-171378ee

Cela fonctionne très bien.

cs0815
la source
Hé, j'ai essayé ça et ça a l'air bien. Mais j'obtiens une erreur lors de l'importation des modules. comme cv2. son écriture aucun module nommé cv2. Comment puis-je résoudre ce problème, avez-vous une idée
İsa GİRİŞKEN
3
Ce lien redirige maintenant vers la liste des sujets. Un seul a mentionné Python et ce n'est pas très pertinent.
Greg Vogel le
3

Définissez WorkingDirectory ou spécifiez le chemin complet du script python dans l'argument

ProcessStartInfo start = new ProcessStartInfo();
start.FileName = "C:\\Python27\\python.exe";
//start.WorkingDirectory = @"D:\script";
start.Arguments = string.Format("D:\\script\\test.py -a {0} -b {1} ", "some param", "some other param");
start.UseShellExecute = false;
start.RedirectStandardOutput = true;
using (Process process = Process.Start(start))
{
    using (StreamReader reader = process.StandardOutput)
    {
        string result = reader.ReadToEnd();
        Console.Write(result);
    }
}
LIU YUE
la source
3

En fait, il est assez facile de faire une intégration entre Csharp (VS) et Python avec IronPython. Ce n'est pas si complexe ... Comme Chris Dunaway l'a déjà dit dans la section réponse, j'ai commencé à construire cette intégration pour mon propre projet. N c'est assez simple. Suivez simplement ces étapes N vous obtiendrez vos résultats.

Étape 1: Ouvrez VS et créez un nouveau projet ConsoleApp vide.

Étape 2: Accédez aux outils -> NuGet Package Manager -> Package Manager Console.

Étape 3: Après cela, ouvrez ce lien dans votre navigateur et copiez la commande NuGet. Lien: https://www.nuget.org/packages/IronPython/2.7.9

Étape 4: Après avoir ouvert le lien ci-dessus, copiez la commande PM> Install-Package IronPython -Version 2.7.9 et collez-la dans NuGet Console dans VS. Il installera les packages de support.

étape 5: c'est mon code que j'ai utilisé pour exécuter un fichier .py stocké dans mon répertoire Python.exe.

using IronPython.Hosting;//for DLHE
using Microsoft.Scripting.Hosting;//provides scripting abilities comparable to batch files
using System;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Net.Sockets;
class Hi
{
private static void Main(string []args)
{
Process process = new Process(); //to make a process call
ScriptEngine engine = Python.CreateEngine(); //For Engine to initiate the script
engine.ExecuteFile(@"C:\Users\daulmalik\AppData\Local\Programs\Python\Python37\p1.py");//Path of my .py file that I would like to see running in console after running my .cs file from VS.//process.StandardInput.Flush();
process.StandardInput.Close();//to close
process.WaitForExit();//to hold the process i.e. cmd screen as output
}
} 

étape 6: enregistrer et exécuter le code

Daulmalik
la source
En tant que développeur, je ne choisis pas de solution car elle est «facile» à mettre en œuvre. J'en choisis un qui fait le travail. IronPython est porté uniquement sur python 2.7 et les travaux sur python 3 sont en cours depuis quelques années maintenant, sans fin en vue. J'espère que votre code python est dans la bonne version.
peter.cyc le
au 27 avril 2020: github.com/IronLanguages/ironpython2/releases/tag/ipy-2.7.10 , mais toujours pas python 3.
Luuk
0

J'ai des problèmes avec stdin/stout- lorsque la taille de la charge utile dépasse plusieurs kilo-octets, elle se bloque. J'ai besoin d'appeler des fonctions Python non seulement avec de courts arguments, mais avec une charge utile personnalisée qui pourrait être importante.

Il y a quelque temps, j'ai écrit une bibliothèque d'acteurs virtuels qui permet de distribuer des tâches sur différentes machines via Redis. Pour appeler du code Python, j'ai ajouté une fonctionnalité permettant d'écouter les messages de Python, de les traiter et de renvoyer les résultats à .NET. Voici une brève description de son fonctionnement .

Il fonctionne également sur une seule machine, mais nécessite une instance Redis. Redis ajoute quelques garanties de fiabilité - la charge utile est stockée jusqu'à ce qu'un travail confirme l'achèvement. Si un ouvré meurt, la charge utile est renvoyée dans une file d'attente de travaux, puis est retraité par un autre travailleur.

VB
la source
Pourquoi n'utiliseriez-vous pas simplement des fichiers d'entrée-sortie? au lieu de laisser les tuyaux gérer tout ce travail.
Inbar Rose
@InbarRose J'avais déjà le système pour .NET, donc l'ajout du traitement Python était très facile et j'ai toujours la distribution et la fiabilité - par exemple, je pourrais publier la charge utile d'une machine Windows et la traiter à partir d'une machine Linux - les fichiers ne fonctionneraient pas dans ce cas Cas.
VB
@InbarRose Et quand la charge utile est vraiment grande, je pourrais passer un nom de fichier sur la même machine ou une référence S3 sur AWS, etc ...
VB