Wie kann ich feststellen, ob meine Batchdatei ausgeführt wird?

8

Ich habe ein Batch-Skript, das über einen AT-Befehl ausgeführt wird und möglicherweise mehrmals ausgeführt wird. Wenn es startet, muss es erkennen, ob es bereits ausgeführt wird, und wenn ja, sofort beenden (das zweite).

  1. Es muss robust sein und funktionieren, wenn die Skripte unerwartet beendet werden (dh ich kann beim Eintritt kein Flag setzen und beim Beenden löschen).
  2. Es muss in einer Remotedesktopsitzung ausgeführt werden
  3. Ich bin mit Powershell v2 auf XP festgefahren, aber es macht mir nichts aus, eine kleine Exe zu schreiben, wenn ich es nicht in Batch / Powershell oder VBS machen kann
  4. Das Skript muss minimiert ausgeführt werden, daher starte ich es mit Start "NAME" / MIN% COMSPEC% / C "MyScript.bat".
  5. Andere cmd-Fenster sind möglicherweise geöffnet, daher muss ich das laufende Skript überprüfen
  6. Das Batch-Skript wird als SYSTEM-Benutzer ausgeführt, ich kann jedoch keine WMI verwenden

Ich habe PowerShell Get-Process verwendet, um MainWindowTitle anzuzeigen, aber dies funktionierte nicht, wenn eine Remoteverbindung zum Computer hergestellt wurde, da das Skript möglicherweise ausgeführt wird, aber in dieser Remoteverbindungsinstanz nicht angezeigt wird. In diesem Fall wird der cmd-Prozess von Get-Process angezeigt, aber der MainWindowTitle ist leer.

Ich habe Get-Process ausprobiert und mir die erweiterte Eigenschaft StartInfo.EnvironmentVariables angesehen, kann jedoch nicht sehen, wie eine env-Variable erstellt wird, damit sie in der Eigenschaft angezeigt wird.

Ich habe überlegt, / WAIT im Startbefehl zu verwenden, dann bleibt das AT geöffnet, bis es beendet ist, aber das Skript, das das AT enthält, wird nicht minimiert

Irgendwelche Ideen?

FrinkTheBrave
quelle
Verwenden Sie eine "Sperrdatei". Erstellen Sie eine Datei, wenn Sie das Skript starten, und löschen Sie sie, wenn Sie fertig sind. Überprüfen Sie vor dem Erstellen, ob es bereits vorhanden ist. Wenn es bereits vorhanden ist, befinden Sie sich in einer zweiten Batchdatei. Beenden Sie das Programm.
DavidPostill
@DavidPostill Ich denke, ihr erster Punkt könnte ein Problem damit sein, denn wenn das Skript fehlschlägt, wird die Sperrdatei nicht entfernt - je nachdem, wie es fehlschlägt, denke ich.
Jonno
@Jonno Das ist leicht zu umgehen. Wenn die Datei bereits vorhanden ist und älter als die aktuelle Zeit ist (aufgrund eines konfigurierbaren Unterschieds), gehen Sie von einem Absturz aus und löschen Sie die Sperrdatei. Dann erstellen Sie eine neue.
DavidPostill
@DavidPostill Könnte eine Lösung sein - OP, haben Sie weitere Informationen darüber, was Ihre Skripte tatsächlich tun? Sollte es innerhalb von Sekunden, Minuten, Stunden fertig sein? Ist es unveränderlich genug, dass ein Zeitbereich verwendet werden könnte? Wie kann es fehlschlagen? Wäre es wahrscheinlich elegant genug, dass ein Löschvorgang für die Sperre nach einem Fehler behandelt werden könnte, unabhängig davon, ob EG, wenn dies fehlschlägt, sich zumindest selbst schließt, sodass Sie einen Stapel aufrufen können, um eine Sperre zu öffnen Skript, dann löschen Sie Ihre Sperre, unabhängig vom Skript Ergebnis?
Jonno
1
Sie können auch die PID als Dateinamen für die Sperrdatei verwenden. Wenn die PID nicht übereinstimmt, beenden Sie die zweite Batchdatei. Sehen Sie, wie eigenen Prozess pid von der Eingabeaufforderung erhalten in Fenster für Code , um die PID zu erhalten. Dieses Skript verwendet auch eine Sperre. "Das Skript richtet eine exklusive Sperre für eine temporäre Datei ein, die die aktuelle Zeit in den Namen einbezieht. Es kann nur zu einer Kollision kommen, wenn zwei gleichnamige Stapelprozesse versuchen, die PID innerhalb desselben Zeitintervalls von 0,01 Sekunden abzurufen In diesem Fall wird nur einer erfolgreich sein. "
DavidPostill

Antworten:

7

Ich glaube, eine Sperrdatei ist die einfachste zuverlässige Lösung. Der Trick besteht darin, sicherzustellen, dass Ihr laufender Batch-Prozess eine exklusive Schreibsperre für die Datei beibehält, bis sie beendet wird. Das Schöne an diesem System ist, dass Windows die Sperre aufhebt, unabhängig davon, aus welchem ​​Grund der Stapel beendet wird.

Sobald Sie eine Sperrdatei haben, müssen Sie feststellen können, ob die Datei derzeit gesperrt ist. Ich beschreibe dies unter Wie überprüfe ich in der Befehlszeile, ob eine bestimmte Datei oder ein bestimmtes Verzeichnis gesperrt ist (von einem Prozess verwendet)?

Ich habe diese primitive, aber effektive Technik verwendet, um einige ziemlich anspruchsvolle Aufgaben mit Windows-Batch auszuführen:

Sie wissen nicht, wo sich die Batchdatei befindet - auf dem Remotecomputer oder dem lokalen Computer oder ob Sie das Skript möglicherweise auf mehreren Computern gleichzeitig ausführen, aber nur auf einem aktiven Prozess pro Computer.

Wenn sich das Batch-Skript auf dem Remote-Computer befindet und Ihr Prozess Schreibzugriff auf das Skript hat, können Sie die Batch-Datei selbst als Sperrdatei verwenden! Sie müssen lediglich eine Unterroutine aufrufen, während Sie ein nicht verwendetes Dateihandle im Append-Modus zum Batch-Skript umleiten. Der CALL schlägt fehl, wenn ein anderer Prozess bereits eine Sperre hat. Die Sperre wird automatisch aufgehoben, wenn das Skript beendet wird (unabhängig davon, wie es beendet wird).

myscript.bat

@echo off
:: Note - this extra call is to avoid a bug with %~f0 when the script
::        is executed with quotes around the script name.
call :getLock
exit /b

:getLock
:: The CALL will fail if another process already has a write lock on the script
call :main 9>>"%~f0"
exit /b

:main
:: Body of your script goes here. Only one process can ever get here
:: at a time. The lock will be released upon return from this routine,
:: or when the script terminates for any reason
exit /b

Wenn sich Ihr Skript auf einem anderen Computer als dem Prozess befindet, der Prozess jedoch jeweils nur auf einem Computer ausgeführt wird, funktioniert das oben Gesagte meiner Meinung nach weiterhin.

Möglicherweise besteht eine bessere Alternative darin, auf jedem Remotecomputer eine dedizierte Sperrdatei einzurichten, die vom Batch-Skript getrennt ist. Anschließend können Sie den Prozess auf beliebig vielen Remotecomputern ausführen.

Dbenham
quelle
Danke für die gründliche Antwort! Aber ich habe eine Alternative verwendet, da ich nicht auf die Idee einer schreibgeschützten Datei gekommen
bin
2

Danke für die Hilfe. Ich habe folgendes implementiert:

Before starting my script (in a wrapper script), check whether the lockfile exists:
  If it does, read the PID out of it.
    If the PID is still a running cmd process, exit my script as another version of it is already running.
    If the PID is not still a running cmd process, delete the lockfile
  Start the script, and create a new lockfile, containing the PID of the started script

When exiting my script, delete the lockfile

Das scheint in Ordnung zu sein, weitere Tests werden es zeigen.

  1. Um laufende Prozesse aufzulisten, verwende ich POWERSHELL Get-Process, da TASKLIST WMI verwendet, das mir, wie bereits erwähnt, nicht zur Verfügung steht.
  2. Um die PID des gestarteten Skripts zu bestimmen, liste ich alle aktuellen cmd-PIDs auf, starte das Skript und liste dann alle cmd-PIDs auf, wobei ich nach der PID suche, die vorher nicht vorhanden war.
FrinkTheBrave
quelle