Laden...

FastCMD - Konsolenprogramme oder Befehle parallel auf verschiedenen Servern ausführen

Erstellt von MadMax.2006 vor 10 Jahren Letzter Beitrag vor 9 Jahren 10.913 Views
M
MadMax.2006 Themenstarter:in
2 Beiträge seit 2014
vor 10 Jahren
FastCMD - Konsolenprogramme oder Befehle parallel auf verschiedenen Servern ausführen

Beschreibung:

Mit diesem Programm können Konsolenprogramme (CMD), oder Befehle parallel auf verschiedenen Servern ausgeführt werden. Minderst Voraussetzung ist eine "server.txt", mit zeilenweise Namen der Server im gleichen Verzeichnis und die Übergabe von dem Dateinamen der Batch, oder eines Befehls, das mehrfach ausgeführt werden soll.

Die Namen der Server werden bei einem Befehl als %%1 und bei Batchdateien als %1 übergeben.

Wenn das Programm läuft, legt es ein "tmp" - Verzeichnis an, in dem die Ausgaben (StdOut, StdErr) in eine Datei (Servernamen) geschrieben werden. Am Ende wird eine Datei erzeugt, mit Datum und Zeit erweitert, in der die Information aus allen Servern zusammen gefasst wird.

Die Hilfe wird wie üblich mit dem Parameter /? oder ohne Angabe von Parametern ausgegeben.

Hier noch die Anzeige der Hilfe:

Startet mehrere Prozesse gleichzeitig mittels Übergabe einer Liste von Server
und der optionalen Vorgabe der maximal gleichzeitig laufenden Prozesse.

Zusätzlich besteht die Möglichkeit vor dem ausführen der Prozesse, die
Erreichbarkeit der Server mittels PING zu ermitteln.

Die Ausgaben (stdOut/stdErr) werden in separaten Protokolldateien erfaßt und
anschließend in einer Protokolldatei zusammengefügt. Fehlermeldungen werden
zusätzlich als Information auf der Konsole ausgegeben.

Die Reihenfolge in der die Server in der Protokolldatei ausgegeben werden,
ist abhängig von der Geschwindigkeit mit der die Server antworten.

Der Name der Protokolldatei hat folgenden Syntax:

    FastCMD dd.mm.yyyy hh-mm-ss.txt  


    Copyright 2014 by Andreas Potsch - Mail an [FastCMD@web.de](mailto:FastCMD@web.de)  


Syntax:   FastCmd [Befehl|Programm] [Server.txt] [pTIMEOUT] [maxAnzahl]  

Befehl/Programm  Befehl|Programm, der|daß mehrfach ausgeführt werden soll.  

server.txt	        Eine Textdatei, in der die Servername zeilenweise angegeben  
    sind, die bei einem Befehl als %%1 und bei einem  
		Batch-Programm als %1 übergeben werden.  
		Wichtig!!! Servernamen dürfen nur einmal in der server.txt  
		vorhanden sein!!!  

pTIMEOUT	Mit diesem Parameters ist es möglich, vor dem ausführen  
		der Befehle|Programme ein PING auf den entsprechenden  
		Server auszuführen. Ist der PING erfolglos, dann wird  
		dies in die Protokolldatei eingetragen und zusätzlich in  
		der Konsole ausgegeben. Die Zahl nach dem Buchstaben legt  
		die Wartezeit (timeout) bis zur Antwort fest. Ist keine  
		Zahl angegeben, wird der Wert auf 1000ms festgelegt.  
		Der Parameter ist optional.  

maxAnzahl Legt die maximale Anzahl der gleichzeitig ausgeführten  
		Befehle/Programme fest. Wird dieser Parameter nicht  
		vorgegeben, starten soviel Befehle/Programme gleichzeitig,  
	        wie Server in der Liste vorhanden sind (maximal 999).  
	        ACHTUNG!! Die Anzahl der gleichzeitig gestarteten Programme,  
	        ist abhängig von dem zur Verfügung stehenden Speicher.  
	        Der Parameter ist optional.  


 Beispiele:  
             	FastCmd  dir \\%%1\c$\Daten server.txt  

		FastCMD  batch.cmd server.txt p5000 50  

		In  der batch.cmd wird %1 als Platzhalter für den jeweiligen  
		Name des Server aus der server.txt übergeben.  
          
          

Programm.cs

 
/*********************************************************************************************************
 *
 * Mit diesem (Consolen/CMD) Programm können mehrere Tasks gleichzeitigt ausgeführt werden.
 * Das Programm ist hauptsächlich für die Arbeit von Systemadministratoren unter dem
 * Betriebssystem Windows gedacht.
 * 
 * Voraussetzungen für das ausführen des Programms ist die Installation von DotNetFramwork 2.0
 * 
 * Hilfe bzw. Syntax ist durch Eingabe von "/?" oder Eingabe keiner/falscher Parameter zu erreichen.
 * Am Ende im Quelltext ist die Ausgabe des Hilfetextes als Funktion zu finden.
 * 
 * Fragen bzw. Anregungen können unter FastCMD@web.de gestellt werden.
 *
\**********************************************************************************************************/

using System;
using System.IO;
using System.Text;
using System.Threading;
using System.Diagnostics;
using System.Collections.Generic;


namespace FastCmd
{
    class Program
    {
        // Zähler die aktuell laufenden Prozesse
        static int prozessRunning = 0;
        
        // Zähler die abgeschlossenen Prozesse
        static int prozessComplete = 0;

        // Zähler die bisher gestarteten Prozesse
        static int prozessStarted = 0;

        // gesamt Anzahl der auszuführenden Prozesse
        static int prozessesExecuted = 0;

        // nimmt den Retunwert der Prozesse auf
        static StringBuilder returnString = null;

        // Hauptprogramm starten
        static int Main(string[] args)
        {
            try
            {
                // Array für die Aufnahme der Server
                List<string> server = new List<string>();

                // Flag, ist Übergabe eine Datei oder ein Befehl
                bool isFile = false;

                // Flag, ist Ping angefordert
                bool isPing = false;

                // Timeout für Ping-Abfrabe. Default 1000ms
                int timeout = 1000;

                // Objekt für den Returnwert der Prozesse erstellen
                returnString = new StringBuilder("");

                // Anzahl der Maximal gleichzeitig ausgeführten Prozesse.
                // Wird durch die Anzahl der Server bzw. der vorgegebenen
                // Anzahl der Max. Prozesse angepaßt
                int MaxProzessCount = 999;

                // Hilfe ausgeben
                if (args.Length == 0)
                {
                    // Hilfe anzeigen
                    Help();
                    return 0;

                }

                // Hilfe ausgeben
                if (args[0] == "/?")
                {
                    // Hilfe anzeigen
                    Help();
                    return 0;

                }

                // wenn wenige als zwei Argumente übergeben werden
                if (args.Length < 2)
                {
                    Console.WriteLine("");
                    Console.WriteLine("");
                    Console.WriteLine("");
                    Console.WriteLine("");
                    Console.WriteLine("");
                    Console.WriteLine("  Bitte mindestens zwei Parameter angeben!");
                    Console.WriteLine("");

                    Help();
                    return 1;

                }

                // Prüfen ob Server.txt-Datei vorhanden ist
                if (!File.Exists(args[1]))
                {
                    Console.WriteLine("");
                    Console.WriteLine("");
                    Console.WriteLine("");
                    Console.WriteLine("");
                    Console.WriteLine("");
                    Console.WriteLine("  Serverdatei ist nicht vorhanden!!!");
                    Console.WriteLine("");

                    Help();
                    return 1;
                }

                // prüfen, ob ein drittes Argument vorhanden und eine Zahl oder ein Buchstabe ist
                if (args.Length > 2)
                {
                    // Ist in dem 3. Argument der Buchstabe "p" enthalten
                    if (args[2].ToLower().ToString().Contains("p") == true)
                    {
                        // ist das 3. Argument länger als ein Zeichen
                        if (args[2].Length > 1)
                        {
                            // Prüfen ob nach dem Buchstaben eine Zahl folgt
                            int num;
                            bool test = int.TryParse(args[2].Substring(1), out num);
                            if (test == true)
                            {
                                // ist die Zahl größer als 0
                                if (num > 0)
                                {
                                    // dann die Zahl für Ping-Timeout übernehmen
                                    timeout = num;
                                }
                                else
                                {
                                    Console.WriteLine("");
                                    Console.WriteLine("");
                                    Console.WriteLine("");
                                    Console.WriteLine("");
                                    Console.WriteLine("");
                                    Console.WriteLine("  Die Zahl nach 'p' muß größer als null sein!!!");
                                    Console.WriteLine("");

                                    Help();
                                    return 1;
                                }

                            }
                            else
                            {
                                Console.WriteLine("");
                                Console.WriteLine("");
                                Console.WriteLine("");
                                Console.WriteLine("");
                                Console.WriteLine("");
                                Console.WriteLine("  Nach dem Buchstaben 'p' muß eine Zahl erfolgen!!!");
                                Console.WriteLine("");

                                Help();
                                return 1;
                            }


                        }
                        // Ping ist angefordert
                        isPing = true;

                    }
                    // Ist das 3. Argument nur eine Zahl
                    else
                    {
                        int num;
                        bool test = int.TryParse(args[2], out num);
                        if (test == false)
                        {
                            Console.WriteLine("");
                            Console.WriteLine("");
                            Console.WriteLine("");
                            Console.WriteLine("");
                            Console.WriteLine("");
                            Console.WriteLine("  Bitte eine Zahl als dritten Parameter eingeben,");
                            Console.WriteLine("  oder mit dem Buchstaben 'p' beginnen lassen!!!");
                            Console.WriteLine("");

                            Help();
                            return 1;

                        }

                        // Beträgt die Zahl maximal 999
                        if (num >= 999)
                        {
                            Console.WriteLine("");
                            Console.WriteLine("");
                            Console.WriteLine("");
                            Console.WriteLine("");
                            Console.WriteLine("");
                            Console.WriteLine("  Bitte keine Zahl über 999 eingeben!!!");
                            Console.WriteLine("");

                            Help();
                            return 1;

                        }

                        // maximale Anzahl der gleichzeitigen laufenden Prozesse übernehmen
                        MaxProzessCount = num;

                    }

                }
                    
                // prüfen, ob ein vierte Argument vorhanden und eine Zahl ist
                if (args.Length > 3)
                {
                    int num;
                    bool test = int.TryParse(args[3], out num);
                    if (test == false)
                    {
                        Console.WriteLine("");
                        Console.WriteLine("");
                        Console.WriteLine("");
                        Console.WriteLine("");
                        Console.WriteLine("");
                        Console.WriteLine("  Bitte eine Zahl als vierten Parameter eingeben!!!");
                        Console.WriteLine("");

                        Help();
                        return 1;

                    }

                    // Beträgt die Zahl maximal 999
                    if (num >= 999)
                    {
                        Console.WriteLine("");
                        Console.WriteLine("");
                        Console.WriteLine("");
                        Console.WriteLine("");
                        Console.WriteLine("");
                        Console.WriteLine("  Bitte keine Zahl über 999 eingeben!!!");
                        Console.WriteLine("");

                        Help();
                        return 1;

                    }

                    // maximale Anzahl der gleichzeitigen laufenden Prozesse übernehmen
                    MaxProzessCount = num;

                }

                
                // Prüfen ob Befehl oder Batchdatei übergeben wurde
                if (File.Exists(args[0]))
                    isFile = true;

                // Objekt Stopuhr erstellen
                Stopwatch stopWatch = new Stopwatch();

                // Stopuhr starten
                stopWatch.Start();

                // Wenn tmp-Verzeichnis vorhanden ist
                if (Directory.Exists(Environment.CurrentDirectory + "\\tmp"))

                    // dann tmp-Verzeichnis löschen
                    Directory.Delete(Environment.CurrentDirectory + "\\tmp", true);

                // Neues tmp-Verzeichnis anlegen
                Directory.CreateDirectory(Environment.CurrentDirectory + "\\tmp");

                // Server aus der Serverdatei einlesen
                string line;
                StreamReader file = new StreamReader(args[1]);

                // Erste Zeile auslesen und überprüfen, ob sie Text enhält
                line = file.ReadLine().ToLower().Trim();
                if ((line == null) || (line == ""))
                {
                    Console.WriteLine("");
                    Console.WriteLine("");
                    Console.WriteLine("");
                    Console.WriteLine("");
                    Console.WriteLine("");
                    Console.WriteLine("  Keine Server in der Serverdatei vorhanden,");
                    Console.WriteLine("  oder die erste Zeile ist leer!!!");
                    Console.WriteLine("");

                    Help();
                    return 1;
                }
                else
                    // Server dem Array hinzufügen
                    server.Add(line);

                // Solange bis alle Zeilen ausgelesen sind
                while ((line = file.ReadLine().ToLower().Trim()) != null)
                {
                    // nur wenn die Zeile Text (Server) enthält
                    if (line != String.Empty)
                    {
                        // Ist der Server vorhanden
                        if (!server.Contains(line))
                        {
                            // nein, Server einfügen
                            server.Add(line);
                        }
                        else
                        {
                            // ja, Meldung ausgeben
                            Console.WriteLine("");
                            Console.WriteLine("");
                            Console.WriteLine("");
                            Console.WriteLine("");
                            Console.WriteLine("");
                            Console.WriteLine("  Servername: " + line.ToUpper() + " ist doppelt vorhanden!!!");
                            Console.WriteLine("");

                            Help();
                            return 1;

                        }

                    }

                }

                // die Anzal aller Prozesse für die Anzeige in der Konsole festlegen (showAktiviti)
                prozessesExecuted = server.Count;

                // Array vom Objekt NewProzess erstellen
                NewProzess[] prozessList = new NewProzess[server.Count];
                
                // Alle Server aus der Datei mit Befehl|Programm in das Array eintragen
                // und die Prozedur für das "Prozesscompleted"-Ereignis zuweisen
                for (int i = 0; i < server.Count; i++)
                {
                    prozessList[i] = new NewProzess(args[0], server[i], isFile, isPing, timeout);
                    prozessList[i].prozessCompleted += 
                        new NewProzess.ProzessCompletedDelegat(newProzess_completeProzess);

                }
                
                // Ist die maximale Anzahl der Server kleiner als die Anzahl der Server in der txt,
                // dann Anzahl der max. Prozesse auf Anzahl der Server anpassen
                if (prozessList.Length < MaxProzessCount)
                    MaxProzessCount = prozessList.Length;

                // Task für die dynamische Anzeige über Taskstatus in der Konsole
                Thread showActiviti = new Thread(new ThreadStart(StartActiviti));

                // Starte Task
                showActiviti.Start();

                // solange nicht alle Server abgearbeitet sind
                while (prozessComplete < server.Count)
                {
                    // Begrenzung der Max. gleichzeitig laufenden Prozesse überprüfen
                    if (prozessRunning < MaxProzessCount)
                    {
                        // ist das Ende der Anzahl der Prozesse erreicht
                        if (prozessStarted < prozessList.Length)
                        {
                            // Prozesse starten
                            prozessList[prozessStarted].StartProzess();

                            // Zähler für die Anzahl laufender Prozesse erhöhen
                            prozessRunning++;

                            // Zähler für die Anzahl der bisher gestarteten Prozesse erhöhen
                            prozessStarted++;
                        }

                    }

                }

                // Format für das Datum in der Protokolldatei
                DateTime time = DateTime.Now;
                string format = "dd.MM.yyyy HH-mm-ss";

                // Name der gesamt Protokolldatei wird gebildet
                string writeFileName = string.Format("{0}\\{1}.txt",
                    Environment.CurrentDirectory,
                    Process.GetCurrentProcess().ProcessName +
                    " " + time.ToString(format));

                // erstellen der gesamt Protokolldatei
                StreamWriter proFile = new StreamWriter(writeFileName);

                // Wenn Puffer voll ist, dann automatisch schreiben
                proFile.AutoFlush = true;
                
                // Alle Textdateien aus dem tmp-Verzeichnis zusammenfügen
                for (int i = 0; i < server.Count; i++)
                {
                    // nimmt den Inhalt der einzelnen Protokolldatei auf und erweitert sie um den Servernamen
                    string allLine = "####### " + server[i] +
                        " ###########################################################################" +
                        Environment.NewLine;

                    // nimmt den Namen der einzelnen Protokolldateinamen auf
                    string readFileName = String.Empty;

                    // Der Name der Protokolldatei im tmp wird gebildet
                    readFileName = string.Format("{0}\\tmp\\{1}.txt",
                        Environment.CurrentDirectory, server[i]);

                    // Prüfen ob die Protokolldatei existiert
                    if (File.Exists(readFileName))
                    {
                        // Öffnen der Protokolldatei
                        StreamReader resfile = new StreamReader(readFileName);

                        // Alle Zeilen auslesen
                        allLine += resfile.ReadToEnd();

                        // In Datei Schreiben
                        proFile.Write(allLine);

                        // Protokolldatei schließen
                        resfile.Close();
                    }
                    // Wenn keine Datei vorhanden ist
                    else
                    {
                        // In Datei Schreiben
                        proFile.Write(allLine);

                        proFile.WriteLine("");
                        proFile.WriteLine("!!! Ausgabedatei nicht vorhanden, oder kein Ping möglich !!!");
                        proFile.WriteLine("");
                    }

                }

                // Reste aus dem Puffer schreiben
                proFile.Flush();

                // Warten bis alles geschrieben ist
                Thread.Sleep(50);

                // Protokolldatei schließen
                proFile.Close();

                // Wenn keine Dateien im Verzeichnis existieren
                if(!File.Exists(Environment.CurrentDirectory + "\\tmp"))

                // dann tmp-Verzeichnis löschen
                    Directory.Delete(Environment.CurrentDirectory + "\\tmp", true);

                // Stopuhr stoppen
                stopWatch.Stop();

                // Zeitdifferenz auslesen
                TimeSpan ts = stopWatch.Elapsed;

                // Ausgabeformat für die Laufzeit festlegen
                string elapsedTime = String.Format("{0:D2}:{1:D2}.{2:D3}",
                    ts.Minutes, ts.Seconds,
                    ts.Milliseconds);

                // Wenn ein Fehler aufgetreten ist
                if (returnString.ToString() != "")
                {
                    // Leerzeile ausgeben
                    Console.WriteLine("");

                    // Fehlermeldung ausgeben
                    Console.WriteLine(returnString);
                }

                // Leerzeilen ausgeben
                Console.WriteLine("");
                Console.WriteLine("");

                // Ausgabe auf der Laufzeit in der Konsole
                Console.WriteLine("Laufzeit: " + elapsedTime);

                // Leerzeilen ausgeben
                Console.WriteLine("");
                Console.WriteLine("");
                Console.WriteLine("");

                return 0;
            }

            // Ausführen im Fehlerfall
            catch (Exception ex)
            {
                Console.WriteLine("");
                Console.WriteLine(ex.Message);
                Console.WriteLine(ex.ToString());
                Console.WriteLine("");

                return 1;
            }

        }

        // Funktion die bei dem Ereignis "Prozesscompleted" aufgerufen wird
        static void newProzess_completeProzess(object sender, prozessReturnArgs e)
        {
            try
            {
                // Zähler für die Anzahl der laufende Prozesse negieren
                prozessRunning--;

                // Anzahl der fertigen Prozesse erhöhen
                prozessComplete++;

                // Prüfen ob ein Fehler aufgetreten ist
                if (e.server != String.Empty)
                {
                    // ist Text in e.ErrMsg vorhanden
                    if (e.ErrMsg != String.Empty)
                    {
                        // Fehlermeldung anhängen
                        returnString.Append(Environment.NewLine +
                            "kein Ping möglich  -> " + e.server);
                    }
                    // ist kein Text in e.ErrMsg vorhanden
                    else
                    {
                        // Fehlermeldung anhängen
                        returnString.Append(Environment.NewLine +
                            "Fehler in Logdatei -> " + e.server);
                    }

                }

            }

            // Ausführen im Fehlerfall
            catch (Exception ex)
            {
                Console.WriteLine("");
                Console.WriteLine(ex.Message);
                Console.WriteLine("");
            }

        }

        // Anzeige der Aktivitäten in der Konsole
        static void StartActiviti()
        {
            try
            {
                // Zähler für die activeSymbol (Array)
                int counter = 0;

                // Zähler für die Verzögerungsschleife
                int delay = 0;

                // Das Flag zeigt an ob alle Prozesse beendet sind
                bool notCompeled = true;

                // Textinhalt der Ausgabezeile aller Aktivitäten
                string line = "";
                Console.WriteLine("");

                // Die "activeSymbol" - Zeichen, die angezeigt werden
                string[] activSymbol = new string[] { "|", "/", "-", "\\" };

                // solange nicht alle Prozesse abgearbeitet sind
                while (notCompeled)
                {
                    // Wenn noch nicht alle Prozesse abgearbeitet sind
                    if (prozessComplete != prozessesExecuted)
                    {
                        // Den aktuellen Status anzeigen mit activeSymbols
                        line = string.Format("Prozesse aktiv: {0,3} -> {1,3} Prozesse beendet ({2,3}%) {3}     \r",
                            prozessRunning,
                            prozessComplete,
                            prozessComplete * 100 / prozessesExecuted,
                            activSymbol[counter]);

                    }
                    // Wenn alle Prozesse abgearbeitet sind
                    else
                    {
                        // Den aktuellen Status (bei 100%) ohne activeSymbol anzeigen
                        line = string.Format("Prozesse aktiv: {0,3} -> {1,3} Prozesse beendet ({2,3}%)         \r",
                            prozessRunning,
                            prozessComplete,
                            prozessComplete * 100 / prozessesExecuted);

                        // Alle Prozesse abgearbeitet
                        notCompeled = false;

                    }

                    // Ausgabe aktueller Status
                    Console.Write(line);

                    // 5 ms warten
                    Thread.Sleep(5);

                    // Verzögerungsschleife
                    if (++delay > 5)
                    {
                        // Zähler zurücksetzen
                        delay = 0;

                        // Hochzählen der "activeSymbol" (Array)
                        if (++counter > activSymbol.Length - 1)

                            // Zähler für "actveSymbol" zurücksetzen
                            counter = 0;
                    }

                }

            }

            // Ausführen im Fehlerfall
            catch (Exception ex)
            {
                Console.WriteLine("");
                Console.WriteLine(ex.Message);
                Console.WriteLine("");
            }

        }

        // Inhalt der Hilfe
        static void Help()
        {
            Console.WriteLine("");
            Console.WriteLine("Startet mehrere Prozesse gleichzeitig mittels Übergabe einer Liste von Server");
            Console.WriteLine("und der optionalen Vorgabe der maximal gleichzeitig laufenden Prozesse.");
            Console.WriteLine("");
            Console.WriteLine("Zusätzlich besteht die Möglichkeit vor dem ausführen der Prozesse, die");
            Console.WriteLine("Erreichbarkeit der Server mittels PING zu ermitteln.");
            Console.WriteLine("");
            Console.WriteLine("Die Ausgaben (stdOut/stdErr) werden in separaten Protokolldateien erfaßt und");
            Console.WriteLine("anschließend in einer Protokolldatei zusammengefügt. Fehlermeldungen werden");
            Console.WriteLine("zusätzlich als Information auf der Konsole ausgegeben.");
            Console.WriteLine("");
            Console.WriteLine("Die Reihenfolge in der die Server in der Protokolldatei ausgegeben werden,");
            Console.WriteLine("ist abhängig von der Geschwindigkeit mit der die Server antworten.");
            Console.WriteLine("");
            Console.WriteLine("Der Name der Protokolldatei hat folgenden Syntax:");
            Console.WriteLine("");
            Console.WriteLine("      " + Process.GetCurrentProcess().ProcessName +
                                " dd.mm.yyyy hh-mm-ss.txt");
            Console.WriteLine("");
            Console.WriteLine("");
            Console.WriteLine("Copyright 2014 by Andreas Potsch - Mail an FastCMD@web.de");
            Console.WriteLine("");
            Console.WriteLine("");
            Console.WriteLine("");
            Console.WriteLine("Syntax:   FastCmd [Befehl|Programm] [Server.txt] [pTIMEOUT] [maxAnzahl]");
            Console.WriteLine("");
            Console.WriteLine(" Befehl/Programm  Befehl|Programm, der|daß mehrfach ausgeführt werden soll.");
            Console.WriteLine("");
            Console.WriteLine(" server.txt       Eine Textdatei, in der die Servername zeilenweise angegeben");
            Console.WriteLine("                  sind, die bei einem Befehl als %%1 und bei einem");
            Console.WriteLine("                  Batch-Programm als %1 übergeben werden.");
            Console.WriteLine("                  Wichtig!!! Servernamen dürfen nur einmal in der server.txt");
            Console.WriteLine("                  vorhanden sein!!!");
            Console.WriteLine("");
            Console.WriteLine(" pTIMEOUT         Mit diesem Parameters ist es möglich, vor dem ausführen");
            Console.WriteLine("                  der Befehle|Programme ein PING auf den entsprechenden");
            Console.WriteLine("                  Server auszuführen. Ist der PING erfolglos, dann wird");
            Console.WriteLine("                  dies in die Protokolldatei eingetragen und zusätzlich in");
            Console.WriteLine("                  der Konsole ausgegeben. Die Zahl nach dem Buchstaben legt");
            Console.WriteLine("                  die Wartezeit (timeout) bis zur Antwort fest. Ist keine");
            Console.WriteLine("                  Zahl angegeben, wird der Wert auf 1000ms festgelegt.");
            Console.WriteLine("                  Der Parameter ist optional.");
            Console.WriteLine("");
            Console.WriteLine(" maxAnzahl        Legt die maximale Anzahl der gleichzeitig ausgeführten");
            Console.WriteLine("                  Befehle/Programme fest. Wird dieser Parameter nicht");
            Console.WriteLine("                  vorgegeben, starten soviel Befehle/Programme gleichzeitig,");
            Console.WriteLine("                  wie Server in der Liste vorhanden sind (maximal 999).");
            Console.WriteLine("                  ACHTUNG!! Die Anzahl der gleichzeitig gestarteten Programme,");
            Console.WriteLine("                  ist abhängig von dem zur Verfügung stehenden Speicher.");
            Console.WriteLine("                  Der Parameter ist optional.");
            Console.WriteLine("");
            Console.WriteLine("");
            Console.WriteLine(" Beispiele:       " + Process.GetCurrentProcess().ProcessName +
                                                    " \"dir \\\\%%1\\c$\\Daten\" server.txt");
            Console.WriteLine("");
            Console.WriteLine("");
            Console.WriteLine("                  " + Process.GetCurrentProcess().ProcessName +
                                                    " batch.cmd server.txt p5000 50");
            Console.WriteLine("");
            Console.WriteLine("                  In der batch.cmd wird %1 als Platzhalter für den jeweiligen");
            Console.WriteLine("                  Name des Server aus der server.txt übergeben.");
            Console.WriteLine("");
            Console.WriteLine("");

        }

    }

}


NewProzess


/*********************************************************************************************************
 *
 * Diese Klasse stellt den Prozess dar, der die Befehle für einen Server ausgeführt.
 * 
 * Von dieser Klasse werden soviele Objekte erstellt, wie Server in der server.txt vohanden sind.
 * Jedes Objekt dieser Klasse wird in einem eigenen Thread ausgeführt und läuft quasi parallel.
 * 
\**********************************************************************************************************/
using System;
using System.IO;
using System.Text;
using System.Threading;
using System.Diagnostics;

namespace FastCmd
{
    class NewProzess
    {
        // Delegat für die Ereignisfunktion
        public delegate void ProzessCompletedDelegat(object sender, prozessReturnArgs e);
        
        // Funktion die das Ereignis darstellt
        public event ProzessCompletedDelegat prozessCompleted;

        // Ojekt zum ausführen eines Prozesses erstellen
        private Process myProzess = new Process();

        // nimmt die Variable "arguments" aus dem Konstruktor auf
        private string arguments = String.Empty;

        // nimmt die Variable "fileName" aus dem Konstruktor auf
        private string fileName = String.Empty;

        // nimmt die Variable "ping" aus dem Konstruktor auf
        private bool isPing = false;

        // nimmt die Variable "timeout" aus dem Konstruktor auf
        private int timeout = 0;

        // nimmt den Stream von stdOutput auf
        private StringBuilder stdOutput = null;

        // Anzahl der durch StdOutput empfangenden Zeilen
        private int numOutputLines = 0;

        // zeigt an ob der Prozess fehlerhaft beendet wurde
        private string taskError = String.Empty;

        // enthält die Fehlermeldung
        private string msgError = String.Empty;

        // Objekt zur Aufnahme der Rückgabewerte (taskError, msgError)
        prozessReturnArgs args = new prozessReturnArgs();

        // Konstruktor für die Klasse mit Übergabe der Parameter
        public NewProzess(string fileName, string arguments, bool isFile, bool isPing, int timeout)
        {
            try
            {
                // Argumente an die globale Variable übergeben
                this.arguments = arguments;

                // Argumente an die globale Variable übergeben
                this.fileName = fileName;

                // Argumente an die globale Variable übergeben
                this.isPing = isPing;

                // Argumente an die globale Variable übergeben
                this.timeout = timeout;
                
                // Ereignisfunktion für StdOutput festlegen
                myProzess.OutputDataReceived += new DataReceivedEventHandler(myProzess_OutputDataReceived);

                // Ereignisfunktion für StdError festlegen
                myProzess.ErrorDataReceived += new DataReceivedEventHandler(myProzess_ErrorDataReceived);

                // Ist der Parameter "arguments" eine Datei
                if (isFile == true)
                {
                    // Programmname
                    myProzess.StartInfo.FileName = fileName;

                    // Programmargumente
                    myProzess.StartInfo.Arguments = arguments;
                }
                // Ist der Parameter "arguments" ein Befehl
                else
                {
                    // Programmname
                    myProzess.StartInfo.FileName = "cmd.exe";

                    // ist %1 in arguments enthalten
                    if (arguments.Contains("%1"))
                    {
                        // Der Platzhalter wird durch den Servernamen ersetzt
                        fileName = fileName.Replace("%1", arguments);
                    }

                    // Damit das Programm an Ende automatisch beendet wird
                    fileName = string.Format(" /c {0}", fileName);

                    // Programmargumente
                    myProzess.StartInfo.Arguments = fileName;

                }

                // Prozess ist nicht sichtbar 
                myProzess.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;

                // StdOutput und StdError empfangen
                myProzess.StartInfo.UseShellExecute = false;

                // StdOutput zulassen
                myProzess.StartInfo.RedirectStandardOutput = true;

                // StdError zulassen
                myProzess.StartInfo.RedirectStandardError = true;

            }

            // Fehlerbehandlung
            catch (Exception ex)
            {
                Console.WriteLine("");
                Console.WriteLine("Prozess: " + fileName + " Argumente: " + arguments);
                Console.WriteLine("");
                Console.WriteLine(ex.Message);
                Console.WriteLine("");
            }

        }

        // Wird ausgelöst, wenn Daten über StdError eingehen
        void myProzess_ErrorDataReceived(object sendingProzess, DataReceivedEventArgs e)
        {
            try
            {
                // wenn Daten von StdError verfügbar sind
                if (!String.IsNullOrEmpty(e.Data))
                {
                    // Zeile hochzählen, wenn Daten verfügbar sind
                    numOutputLines++;

                    // Text dem StringBuilder hinzufügen
                    stdOutput.Append(Environment.NewLine + "ERROR: " + e.Data);

                    // Festlegen, das ein Fehler aufgetreten ist
                    taskError = arguments;
                }

            }

            // Fehlerbehandlung
            catch (Exception ex)
            {
                Console.WriteLine("");
                Console.WriteLine("Prozess: " + fileName + " Argumente: " + arguments);
                Console.WriteLine("");
                Console.WriteLine(ex.Message);
                Console.WriteLine("");
            }

        }

        // Wird ausgelöst, wenn Daten über StdOutput eingehen
        void myProzess_OutputDataReceived(object sendingProzess, DataReceivedEventArgs e)
        {
            try
            {
                // wenn Daten von StdOutput verfügbar sind
                if (!String.IsNullOrEmpty(e.Data))
                {
                    // Zeile hochzählen, wenn Daten verfügbar sind
                    numOutputLines++;

                    // Text dem StringBuilder hinzufügen
                    stdOutput.Append(Environment.NewLine + e.Data);
                }

            }

            // Fehlerbehandlung
            catch (Exception ex)
            {
                Console.WriteLine("");
                Console.WriteLine("Prozess: " + fileName + " Argumente: " + arguments);
                Console.WriteLine("");
                Console.WriteLine(ex.Message);
                Console.WriteLine("");
            }

        }

        // Funktion zum starten des Prozesses in dem Objekt
        public void StartProzess()
        {
            try
            {
                // Thread erstellen
                Thread myThread = new Thread(new ThreadStart(StartTask));

                // Thread starten
                myThread.Start();
            }

            // Die Ausnahme wird ausgelöst, wenn ein NULL-Verweis an die Methode übergeben wird.
            catch (System.ArgumentNullException ex)
            {
                Console.WriteLine("");
                Console.WriteLine("Prozess: " + fileName + " Argumente: " + arguments);
                Console.WriteLine("");
                Console.WriteLine(ex.Message);
                Console.WriteLine("");
            }

            // Die Ausnahme wird ausgelöst, wenn nicht genügend Arbeitsspeicher zur Verfügung steht.
            catch (System.OutOfMemoryException ex)
            {
                Console.WriteLine("");
                Console.WriteLine("Prozess: " + fileName + " Argumente: " + arguments);
                Console.WriteLine("");
                Console.WriteLine(ex.Message);
                Console.WriteLine("");
            }

            // Die Ausnahme wird bei allen anderen Fehlern ausgelöst.
            catch (Exception ex)
            {
                Console.WriteLine("");
                Console.WriteLine("Prozess: " + fileName + " Argumente: " + arguments);
                Console.WriteLine("");
                Console.WriteLine(ex.Message);
                Console.WriteLine("");
            }

        }

        // eigenen Task für den Prozess starten
        private void StartTask()
        {
            try
            {
                // Objekt erstellen
                stdOutput = new StringBuilder("");

                // wird ein Ping angefordert
                if (isPing == true)
                {
                    // Ping Objekt erstellen und ausführen
                    PingIP ping = new PingIP(arguments, timeout);

                    // Konnte ein Ping erfolgreich empfangen werden
                    if (ping.result == true)
                    {
                        // den Prozess starten
                        myProzess.Start();

                        // Startet asynchrones lesen des Outputstreams
                        myProzess.BeginOutputReadLine();

                        // Startet asynchrones lesen des Errorstreams
                        myProzess.BeginErrorReadLine();

                        // warten bis der Prozess beendet ist, maximal 10 Minuten
                        myProzess.WaitForExit(600000);

                        // Sind Daten von StdOutput vorhanden, dann in Textdatei schreiben
                        if (numOutputLines > 0)
                        {
                            // Dateiname für die StdOutput Rückmeldung festlegen
                            string place = string.Format("{0}\\tmp\\{1}.txt", Environment.CurrentDirectory, arguments);

                            // Objekt für den Stream von StdOutput erzeugen
                            StreamWriter file = new StreamWriter(place);

                            // Leerzeilen hinzufügen
                            stdOutput.Append(Environment.NewLine + Environment.NewLine);

                            // StdOutput in Textdatei schreiben
                            file.Write(stdOutput);

                            // Reste aus dem Puffer schreiben
                            file.Flush();

                            // Warten bis alles geschrieben ist
                            Thread.Sleep(50);

                            // StreamWriter schließen
                            file.Close();

                        }

                    }
                    // Ping nicht erfolgreich gewesen
                    else
                    {
                        taskError = arguments;
                        msgError = "Server nicht erreichbar !!!";
                    }

                }
                // Es wird kein Ping angefordert
                else
                {
                    // den Prozess starten
                    myProzess.Start();

                    // Startet asynchrones lesen des Outputstreams
                    myProzess.BeginOutputReadLine();

                    // Startet asynchrones lesen des Errorstreams
                    myProzess.BeginErrorReadLine();

                    // warten bis der Prozess beendet ist
                    myProzess.WaitForExit();

                    // Sind Daten von StdOutput vorhanden, dann in Textdatei schreiben
                    if (numOutputLines > 0)
                    {
                        // Dateiname für die StdOutput Rückmeldung festlegen
                        string place = string.Format("{0}\\tmp\\{1}.txt", Environment.CurrentDirectory, arguments);

                        // Objekt für den Stream von StdOutput erzeugen
                        StreamWriter file = new StreamWriter(place);

                        // Leerzeilen hinzufügen
                        stdOutput.Append(Environment.NewLine + Environment.NewLine);

                        // StdOutput in Textdatei schreiben
                        file.Write(stdOutput);

                        // Reste aus dem Puffer schreiben
                        file.Flush();

                        // Warten bis alles geschrieben ist
                        Thread.Sleep(50);

                        // StreamWriter schließen
                        file.Close();

                    }

                }

                // Status wird als Argument übergeben
                args.server = taskError;

                // Fehlermeldung wird als Argument übergeben
                args.ErrMsg = msgError;

                // Prozess beendet, Ereignis auslösen
                OnEvent(args);

            }

            // Fehlerbehandlung
            catch (Exception ex)
            {
                Console.WriteLine("");
                Console.WriteLine("Prozess: " + fileName + " Argumente: " + arguments);
                Console.WriteLine("");
                Console.WriteLine(ex.Message);
                Console.WriteLine("");
            }

        }

        // Funktion löst das Ereigis "prozessCompleted" aus
        protected virtual void OnEvent(prozessReturnArgs e)
        {
            try
            {
                // Ereignisfunktion für StdOutput lösen
                myProzess.OutputDataReceived -= new DataReceivedEventHandler(myProzess_OutputDataReceived);

                // Ereignisfunktion für StdError lösen
                myProzess.ErrorDataReceived -= new DataReceivedEventHandler(myProzess_ErrorDataReceived);

                // sind Empfänger für das Ereignis vorhanden
                if (prozessCompleted != null)

                    // Ereignis auslösen
                    prozessCompleted(this, e);
            }

            // Fehlerbehandlung
            catch (Exception ex)
            {
                Console.WriteLine("");
                Console.WriteLine("Prozess: " + fileName + " Argumente: " + arguments);
                Console.WriteLine("");
                Console.WriteLine(ex.Message);
                Console.WriteLine("");
            }

        }
        
    }

    // Klasse für die Rückgabeparameter
    public class prozessReturnArgs : EventArgs
    {
        public string server { get; set; }
        public string ErrMsg { get; set; }
    }

}


PingIP.cs


/*********************************************************************************************************
 *
 * Diese Klasse stellt ein Objekt dar, welches ein Ping auf den übergebenen Servernamen ausführt.
 * 
 * Quelle:
 * http://msdn.microsoft.com/en-us/library/system.net.networkinformation.ping%28v=vs.80%29.aspx
 * 
\**********************************************************************************************************/
using System;
using System.Net;
using System.Text;
using System.Threading;
using System.Net.NetworkInformation;


namespace FastCmd
{
    class PingIP
    {
        // Ergebnis der Ping-Abfrage 
        bool status = false;

        // Eigenschaft der Klasse, enthält das Ergebnis aus der Ping-Abfrage
        public bool result
        {
            get { return status; }
        }

        // Konstruktor mit übergabe der Servers und timeout
        public PingIP(string hostName, int timeout)
        {
            try
            {
                IPHostEntry host;
                IPAddress Adresse = null;

                // Die IP-Adresse des Servers wird abgefragt
                host = Dns.GetHostEntry(hostName);

                // Liste aller IP-Adressen durchlaufen
                foreach (IPAddress ip in host.AddressList)
                    Adresse = ip;

                AutoResetEvent waiter = new AutoResetEvent(false);

                Ping pingSender = new Ping();

                // When the PingCompleted event is raised,
                // the PingCompletedCallback method is called.
                pingSender.PingCompleted += new PingCompletedEventHandler(PingCompletedCallback);

                // Create a buffer of 32 bytes of data to be transmitted.
                string data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
                byte[] buffer = Encoding.ASCII.GetBytes(data);

                // Set options for transmission:
                // The data can go through 64 gateways or routers
                // before it is destroyed, and the data packet
                // cannot be fragmented.
                PingOptions options = new PingOptions(64, true);

                // Send the ping asynchronously.
                // Use the waiter as the user token.
                // When the callback completes, it can wake up this thread.
                pingSender.SendAsync(Adresse, timeout, buffer, options, waiter);

                // Prevent this example application from ending.
                // A real application should do something useful
                // when possible.
                waiter.WaitOne();
            }

            // Fehlerbehandlung
            catch (Exception)
            {
                // Ping nicht erfolgreich
                status = false;
            }

        }

        public void PingCompletedCallback(object sender, PingCompletedEventArgs e)
        {
            // If the operation was canceled or an error occurred, generate a message to the user.
            if ((e.Cancelled)||(e.Error != null))
            {
                // Ping nicht erfolgreich
                status = false;

                // Let the main thread resume. 
                // UserToken is the AutoResetEvent object that the main thread 
                // is waiting for.
                ((AutoResetEvent)e.UserState).Set();

                return;
            }

            // Ping erfolgreich
            status = true;

            // Let the main thread resume.
            ((AutoResetEvent)e.UserState).Set();

        }

    }

}


Anregungen und Fragen können unter FastCMD@web.de gestellt werden.

Schlagwörter: FastCMD Admin Betriebsführung Multi Thread Parallel

16.806 Beiträge seit 2008
vor 10 Jahren

Hi,

danke für den Code. Ein paar Anmerkeungen von mir

Kommentare passen oft nicht zum Code:

// prüfen, ob ein drittes Argument vorhanden und eine Zahl oder ein Buchstabe ist
if (args.Length > 2)
{

Manche Prüfungen basieren wohl auf nicht ganz 100%tigem Verständnis von .NET und sind unnötig aufwändig

// Ist in dem 3. Argument der Buchstabe "p" enthalten
if (args[2].ToLower().ToString().Contains("p") == true)
{

.. oder Fehleranfällig (Path-Klasse verwenden!)

// Wenn tmp-Verzeichnis vorhanden ist
if (Directory.Exists(Environment.CurrentDirectory + "\\tmp"))

Sehr unglücklich gewähler Klassenname

// Array vom Objekt NewProzess erstellen
NewProzess[] prozessList = new NewProzess[server.Count];

Streams packt man immer in usings, wenn möglich

// Öffnen der Protokolldatei
StreamReader resfile = new StreamReader(readFileName);

Sowas ist nicht nur schlechter stil; es macht auch keinen Sinn.
Wenn auf irgendwas gewartet werden muss, dann arbeitet man mit Events o.ä.. Insgesamt fehler dem Code ein deutlich Touch OOP.

// Warten bis alles geschrieben ist
Thread.Sleep(50);

// 5 ms warten
Thread.Sleep(5);

Insgesamt eine gute Basis und eine gute Idee; wäre mir aber viel zu fehleranfällig für den produktiven Betrieb.
Zudem gibt es bessere Möglichkeiten um zB CommandLine zu parsen, wie das NuGet-Package CommandLineParser.

M
MadMax.2006 Themenstarter:in
2 Beiträge seit 2014
vor 10 Jahren

Hi,

danke für die konstruktiven Anmerkungen.

Was den Quellcode angeht bin ich für Verbesserungsvorschläge empfänglich.
Es können natürlich auch Änderungen/Verbesserungen an dem Code vorgenommen und dann neu gepostet werden, hoffentlich.

Alles und jeder ist fehlbar. Nichts und niemand ist unfehlbar.

Gruß
Andreas

P
157 Beiträge seit 2014
vor 9 Jahren

Hallöchen,

solche Programme sind ganz gut um sich in neue Themen einzuarbeiten. Wenn das noch aktuell ist und für dich interessant, versuch mal deinen Code selbst zu überarbeiten. Du kannst vieles in kleine Methoden auslagern (zum beispiel die Konsolenausgabe der Exceptions). Dadurch dürfte dein gesamter Quelltext auf ca 20 % des jetzigen schrumpfen.

Was die Benennung angeht...solange man weiß wofür etwas da ist reichts immer. Du musst selbst deinen Quellcode nach nem Jahr noch ohne Nachdenken lesen können, das machts auch viel leichter für andere, wenn die nicht raten müssen.

So als kleiner Anreiz für eine zusätzliche Optimierung:

Für die Kommandozeilen kannst du dir sowas wie nen "Wrapper" schreiben. Eine Klasse die in etwa so aussieht:

class CommandLineParams
{
    public CommandLineParams(string[] args)
    {
       // das hier schreibt man etwas eleganter aber sollte für dich verständlich sein
        foreach (var argument in args)
           ShowHelp = argument.ToLower() == "/?"; // hier kommt true oder false raus

    }
    //Gegen diese Eigenschaft kannst du dann prüfen ob du eine Hilfe anzeigen möchtest
    public bool ShowHelp {get; protected set;}
}

Nun kannst du direkt nach dem Programmstart deine args aus Main() parsen (->übersetzen) und dein Programm wird später viel einfach zu verstehen sein:

var commandLineParams = new CommandLineParams(args);
if (commandLineParams.ShowHelp)
{
   ShowHelp();
}

So hast du deinen Papyrusrollenquellcode (weils so schön untereinandersteht) viel leichter im Griff, wenn jemand nähmlich mal ein Programm wiederverwendet wird er oder sie vielleicht nach einer Methode suchen, die ShowHelp entspricht.

vg

Wenn's zum weinen nicht reicht, lach drüber!