
Tengo un script por lotes que se ejecuta desde un comando AT y es posible que se ejecute más de una vez. Cuando se inicia, necesito que detecte si ya se está ejecutando y, de ser así, salir (el segundo) inmediatamente.
- Debe ser robusto y manejar si los scripts salen inesperadamente (es decir, no puedo establecer una marca al entrar y borrarla al salir).
- Debe ejecutarse en una sesión de Escritorio remoto.
- Estoy atascado en XP con Powershell v2, pero no me importa escribir un pequeño archivo ejecutable si no puedo hacerlo en lotes/powershell o vbs.
- El script debe ejecutarse minimizado, así que lo inicio con Start "NAME" /MIN %COMSPEC% /C "MyScript.bat"
- Es posible que otras ventanas de cmd estén abiertas, por lo que necesito verificar el script en ejecución.
- El script por lotes se ejecuta como usuario del SISTEMA, pero no puedo usar ningún WMI
Estaba usando PowerShell Get-Process para ver MainWindowTitle, pero esto no funcionó cuando me conecté de forma remota a la computadora, ya que es posible que el script se esté ejecutando, pero no se muestra en esta instancia de conexión remota. En este caso, Get-Process ve el proceso cmd, pero MainWindowTitle está en blanco.
Probé Get-Process y miré la propiedad StartInfo.EnvironmentVariables expandida, pero no veo cómo crear una var env para que aparezca en la propiedad.
Pensé en usar /WAIT en el comando de inicio, luego el AT permanecerá abierto hasta que finalice, pero el script que contiene el AT no está minimizado.
¿Algunas ideas?
Respuesta1
Creo que un archivo de bloqueo es la solución confiable más simple. El truco consiste en asegurarse de que el proceso por lotes en ejecución mantenga un bloqueo de escritura exclusivo en el archivo hasta que finalice. La belleza de este sistema es que Windows liberará el bloqueo sin importar el motivo por el que finalice el lote.
Una vez que tenga un archivo bloqueado, necesitará una forma de detectar si el archivo está actualmente bloqueado. Describo cómo hacer esto en¿Cómo verificar en la línea de comandos si un archivo o directorio determinado está bloqueado (utilizado por cualquier proceso)?
He utilizado esta técnica primitiva, pero efectiva, para realizar algunas tareas bastante sofisticadas con Windows por lotes:
- Ejecución paralela de una lista de tareas, limitando el número total de procesos simultáneos.
- ¿Cómo se comparten archivos de registro en Windows?
- Serializar la ejecución de symstore a través de Powershell o BATCH
No tiene claro dónde reside el archivo por lotes: en la máquina remota, en la máquina local, o si puede estar ejecutando el script en varias máquinas simultáneamente, pero solo un proceso activo por máquina.
Si el script por lotes está en la máquina remota y su proceso tiene acceso de escritura al script, entonces puede usar el archivo por lotes como archivo de bloqueo. Simplemente necesita llamar a una subrutina: mientras redirige un identificador de archivo no utilizado al script por lotes usando el modo agregar. La LLAMADA fallará si otro proceso ya tiene un bloqueo. El bloqueo se liberará automáticamente cuando finalice el script (independientemente de cómo finalice).
miscript.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
Si su secuencia de comandos está en una máquina diferente a la del proceso, pero solo tiene el proceso ejecutándose en una máquina a la vez, entonces creo que lo anterior seguirá funcionando.
Quizás una mejor alternativa sea establecer un archivo de bloqueo dedicado en cada máquina remota, separado del script por lotes. Luego podrá ejecutar el proceso en tantas máquinas remotas como desee.
Respuesta2
Gracias por la ayuda. He implementado lo siguiente:
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
Parece funcionar bien, más pruebas lo dirán.
- Para enumerar los procesos en ejecución uso POWERSHELL Get-Process ya que TASKLIST usa WMI, que como dije anteriormente, no está disponible para mí.
- Para determinar el PID del script iniciado, enumero todos los PID de cmd actuales, inicio el script, luego enumero todos los PID de cmd, buscando el que no estaba allí antes.
Respuesta3
Creé un complemento automatizado llamado CMDS que puedes usar aquí:¿Cómo puedo ver si se está ejecutando un determinado archivo por lotes y obtener el PID?Sin embargo, no estoy seguro de si esto funcionará con XP.
Comando de ejemplo:CMDS /TS "Your Batch File Title"
También tiene un modo gui para ser utilizado por sí mismo.
Aquí está el archivo de ayuda: