Saltar al contenido

retrasar un archivo por lotes en menos de un segundo?

Recabamos en el mundo on line para así darte la solución a tu duda, si continúas con inquietudes puedes dejarnos un comentario y te respondemos porque estamos para servirte.

Solución:

Hace algún tiempo publiqué un método que proporciona una sincronización precisa con intervalos de retardo de 15 milisegundos en adelante. Esta es una copia de la publicación completa.


Creo que logré un retraso de milisegundos con una sincronización precisa cuando el retraso es pequeño. Usé una solución híbrida Batch-JScript con el método WScript.Sleep, pero para evitar el retraso de carga de la sección JScript cada vez que se usa, ambas partes deben estar activas al mismo tiempo. El proceso de JScript toma la demora en milisegundos, realiza la demora y envía una señal a la sección Batch. El proceso Batch envía el número de milisegundos a JScript y espera la señal. La forma de lograr esta comunicación bidireccional es a través del método WshShwll.Exec de JScript que tiene acceso a los flujos Stdin y Stdout del proceso Batch.

@if (@CodeSection == @Batch) @then


@echo off
setlocal EnableDelayedExpansion
if defined restart goto secondPart

rem First part: execute JScript section, so it re-execute this Batch file
set restart=true
CScript //nologo //E:JScript "%~F0" "%~F0"
goto :EOF

:secondPart

rem To do a delay, use: "echo #millisecs" followed by "set /P ="; use "echo 0" to end
rem To display data in the screen, use:  echo data > CON
rem To read data from keyboard, use set /P "data=Prompt: " < CON > CON

set runs=10
For %%t in (5 10 15 20 30 50 100 250 500 1000) do (

   set time_idle_ms=%%t
   (
   set t0=!time!
   for /L %%p in (1,1,%runs%) do echo %%t& set /P =
   set t1=!time!
   )

   for /F "tokens=1-8 delims=:.," %%a in ("!t0: =0!:!t1: =0!") do (
      set /a "a=(((1%%e-1%%a)*60)+1%%f-1%%b)*6000+1%%g%%h-1%%c%%d, a+=(a>>31) & 8640000"
   )

   set /a average_time=a*10/runs
   echo(Input:!time_idle_ms! ms - Output: Average time !average_time! ms > CON
)

rem Send the signal to end JScript section
echo 0
goto :EOF


@end


// JScript section

// Restart this Batch file with access to its Stdin and Stdout streams
var WshShell = new ActiveXObject("WScript.Shell");
var BatchFile = WshShell.Exec('"'+WScript.Arguments(0)+'"'), delay;

// Get delay, wait and send CR until delay equ 0
while ((delay = BatchFile.Stdout.ReadLine()) != "0" ) 
   WScript.Sleep(delay);
   BatchFile.Stdin.WriteLine();

Producción:

Input:5 ms - Output: Average time 15 ms
Input:10 ms - Output: Average time 16 ms
Input:15 ms - Output: Average time 15 ms
Input:20 ms - Output: Average time 32 ms
Input:30 ms - Output: Average time 31 ms
Input:50 ms - Output: Average time 63 ms
Input:100 ms - Output: Average time 109 ms
Input:250 ms - Output: Average time 250 ms
Input:500 ms - Output: Average time 500 ms
Input:1000 ms - Output: Average time 1000 ms

Otra prueba en Windows 8.1 32 bit – 3.2 GHz

Input:5 ms - Output: Average time 14 ms
Input:10 ms - Output: Average time 16 ms
Input:15 ms - Output: Average time 15 ms
Input:20 ms - Output: Average time 31 ms
Input:30 ms - Output: Average time 32 ms
Input:50 ms - Output: Average time 61 ms
Input:100 ms - Output: Average time 110 ms
Input:250 ms - Output: Average time 250 ms
Input:500 ms - Output: Average time 501 ms
Input:1000 ms - Output: Average time 1000 ms

EDITAR: pathping prueba agregada

Solo para completar este tema, hice una prueba de tiempo usando pathping y el mismo código utilizado para probar mi método. Aquí está:

@echo off
setlocal EnableDelayedExpansion

set runs=10
For %%t in (5 10 15 20 30 50 100 250 500 1000) do (

   set time_idle_ms=%%t
   (
   set t0=!time!
   for /L %%p in (1,1,%runs%) do pathping 127.0.0.1 -n -q 1 -p %%t >nul
   set t1=!time!
   )

   for /F "tokens=1-8 delims=:.," %%a in ("!t0: =0!:!t1: =0!") do (
      set /a "a=(((1%%e-1%%a)*60)+1%%f-1%%b)*6000+1%%g%%h-1%%c%%d, a+=(a>>31) & 8640000"
   )

   set /a average_time=a*10/runs
   echo(Input:!time_idle_ms! ms - Output: Average time !average_time! ms

)

El resultado muestra que pathping es no confiable para pequeños tiempos de retraso:

Input:5 ms - Output: Average time 48 ms
Input:10 ms - Output: Average time 47 ms
Input:15 ms - Output: Average time 47 ms
Input:20 ms - Output: Average time 62 ms
Input:30 ms - Output: Average time 63 ms
Input:50 ms - Output: Average time 93 ms
Input:100 ms - Output: Average time 141 ms
Input:250 ms - Output: Average time 281 ms
Input:500 ms - Output: Average time 532 ms
Input:1000 ms - Output: Average time 1031 ms

pathping 127.0.0.1 -n -q 1 -p 100 >nul

Aquí hubo una discusión sobre la espera / demora adecuada en milisegundos. De acuerdo con las últimas publicaciones, el pathping debería hacer el trabajo durante los milisegundos.

con mshta (esto esperará 500 milisegundos):

start "" /wait /min /realtime mshta "javascript:setTimeout(function()close();,500)"

Otra forma con el ejecutable .NET autocompilado:

@if (@X)==(@Y) @end /* JScript comment
@echo off
setlocal
::del %~n0.exe /q /f
::
:: For precision better call this like
:: call waitMS 500
:: in order to skip compilation in case there's already built .exe
:: as without pointed extension first the .exe will be called due to the ordering in PATEXT variable
::
::
for /f "tokens=* delims=" %%v in ('dir /b /s /a:-d  /o:-n "%SystemRoot%Microsoft.NETFramework*jsc.exe"') do (
   set "jsc=%%v"
)

if not exist "%~n0.exe" (
    "%jsc%" /nologo /w:0 /out:"%~n0.exe" "%~dpsfnx0"
)


%~n0.exe %*

endlocal & exit /b %errorlevel%


*/


import System;
import System.Threading;

var arguments:String[] = Environment.GetCommandLineArgs();
function printHelp()
    Console.WriteLine(arguments[0]+" N");
    Console.WriteLine(" N - milliseconds to wait");
    Environment.Exit(0);    


if(arguments.length<2)
    printHelp();


try
    var wait:Int32=Int32.Parse(arguments[1]);
    System.Threading.Thread.Sleep(wait);
catch(err)
    Console.WriteLine('Invalid Number passed');
    Environment.Exit(1);

A continuación, tengo tres soluciones de script puro diferentes para proporcionar retrasos de menos de un segundo dentro de un script por lotes. Todas estas soluciones tienen un retraso mínimo que varía de una máquina a otra. Siempre que se exceda el retardo mínimo, cada uno de ellos generalmente tiene una precisión de 10 milisegundos.

Utilicé el arnés de prueba de Aacini para probar la precisión de los tres métodos para que los resultados se puedan comparar directamente con sus resultados.

Macro de lote

Esta solución por lotes pura es muy simple en concepto. Obtengo la hora actual (con una precisión de 1/100 de segundo), agrego la demora y espero esa nueva hora dentro de un circuito cerrado.

He empaquetado esta lógica en un lote avanzado macro técnica que es difícil de entender y desarrollar, pero fácil de usar. Usando un macro, CALL no introduce ningún retraso. Ver lote actual macro sintaxis y desarrollo histórico de macros por lotes si desea obtener más información sobre macro teoría y cómo escribir la tuya propia.

Esta solución por lotes pura tiene el retardo mínimo más pequeño de los tres. En las dos máquinas que he probado, el retardo mínimo osciló entre ~ 15 y ~ 30 mseg. Esta macro solo admite retrasos de menos de 24 horas.

El mayor inconveniente con esto macro es que consume recursos de la CPU, vinculando una sola máquina con CPU con un solo núcleo al 100% de uso durante el retraso.

@echo off
setlocal disableDelayedExpansion

::********  Define the @delay macro  ****************

:: define LF as a Line Feed (newline) character
set ^"LF=^

^" Above empty line is required - do not remove

:: define a newline with line continuation
set ^"n=^^^%LF%%LF%^%LF%%LF%^^"

set @delay=for %%# in (1 2) do if %%#==2 (%n%
  for /f "tokens=1-4 delims=:.," %%a in ("!time: =0!") do set /a "t1=(((1%%a*60)+1%%b)*60+1%%c)*100+1%%d-36610100, arg1/=10"%n%
  cmd /v:on /c for /l %%. in (^^^) do @for /f "tokens=1-4 delims=:.," %%a in ("^!time: =0^!"^^^) do @set /a "t2=(((1%%a*60)+1%%b)*60+1%%c)*100+1%%d-36610100,tDiff=t2-t1"^^^>nul^^^&(if ^^^^!tDiff^^^^! lss 0 set /a tDiff+=8640000^^^>nul^^^)^^^&if ^^^^!tDiff^^^^! geq ^^^^!arg1^^^^! exit%n%
  endlocal%n%
) else setlocal enableDelayedExpansion^&set arg1=


::**********  Demonstrate usage  ********************
echo Delaying for 1.25 seconds ...
%@delay% 1250
echo done.
echo(


::***********  Testing accuracy  ********************
setlocal enableDelayedExpansion
echo Testing accuracy:
set runs=10
for %%t in (10 20 30 40 50 70 100 250 500 1000) do (

  (
  set t0=!time!
  for /l %%p in (1,1,%runs%) do %@delay% %%t
  set t1=!time!
  )

  for /f "tokens=1-8 delims=:.," %%a in ("!t0: =0!:!t1: =0!") do (
    set /a "a=(((1%%e-1%%a)*60)+1%%f-1%%b)*6000+1%%g%%h-1%%c%%d, a+=(a>>31) & 8640000"
  )

  set /a average_time=a*10/runs
  echo(Input:%%t ms - Output: Average time !average_time! ms
)

- Resultados de muestra -

Delaying for 1.25 seconds ...
done.

Testing accuracy:
Input:10 ms - Output: Average time 14 ms
Input:20 ms - Output: Average time 20 ms
Input:30 ms - Output: Average time 30 ms
Input:40 ms - Output: Average time 40 ms
Input:50 ms - Output: Average time 50 ms
Input:70 ms - Output: Average time 70 ms
Input:100 ms - Output: Average time 100 ms
Input:250 ms - Output: Average time 250 ms
Input:500 ms - Output: Average time 500 ms
Input:1000 ms - Output: Average time 1000 ms

SLEEP.BAT - JScript híbrido / Batch (local agnóstico)

Esta es mi solución favorita. LLAMO a mi utilidad SLEEP.BAT, pasando el retardo deseado, más el %time% en el momento de la LLAMADA. La secuencia de comandos por lotes llama a JScript incrustado dentro del mismo archivo, que resta el tiempo de LLAMADA del tiempo actual para determinar cuánto tiempo ya ha transcurrido, y luego resta este valor del tiempo de demora para calcular cuánto tiempo debe dormir la secuencia de comandos. Esta solución utiliza el método WScript.sleep (), que está controlado por eventos y no consume recursos de la CPU.

El script por lotes proporcionará su propio %time% valor si no se pasa, pero entonces la precisión puede sufrir un poco debido a la cantidad de tiempo que lleva LLAMAR a la utilidad.

CSCRIPT tarda un tiempo significativo en inicializarse, por lo que el retardo mínimo es mayor que el macro solución. En mis dos máquinas, el retardo mínimo osciló entre ~ 30 y ~ 55 mseg.

SLEEP.BAT debería funcionar en cualquier máquina con Windows, independientemente de cómo su ubicación muestre la fecha y la hora. El único problema que tengo con esta utilidad es que puede dar un retraso incorrecto si se llama el instante antes del cambio del horario estándar al horario de verano, o viceversa.

SLEEP.BAT

@if (@X)==(@Y) @end /* harmless hybrid line that begins a JScript comment
@goto :batch
:::
:::SLEEP.BAT  msec  [%time%]
:::SLEEP.BAT  /?
:::
:::  Suspend processing for msec milliseconds. The optional %time% argument
:::  can be added to improve timing accuracy. If called within a FOR loop,
:::  then !time! should be used instead, after enabling delayed expansion.
:::
:::  There is a startup time for SLEEP.BAT that limits the shortest delay
:::  possible. The startup time varies from machine to machine. Delays longer
:::  than the minimum are usually accurate to within 10 msec if the %time%
:::  argument is provided. One exception is when SLEEP.BAT is called the
:::  instant before changing from standard to daylight savings time, in which
:::  case the delay is extended by the startup time. The other exception occurs
:::  when changing from daylight savings to standard, in which case the delay
:::  never exceeds the startup time.
:::
:::  A single /? argument displays this help.
:::
:::  SLEEP.BAT Version 1.0 - written by Dave Benham
:::

============= :Batch portion ===========
@echo off
if "%~1" equ "/?" (
  for /f "tokens=* delims=:" %%A in ('findstr "^:::" "%~f0"') do @echo(%%A
  exit /b 0
) else cscript //E:JScript //nologo "%~f0" %* %time%
exit /b

============ JScript portion ==========*/
try 
  var arg2 = WScript.Arguments.Item(1).split(/[:.,]/);
  var start = new Date();
  if (start.getHours()0) WScript.sleep( (adjustedDelay>delay) ? delay : adjustedDelay );
  WScript.Quit(0);
 catch(e) 
  WScript.Stderr.WriteLine("SLEEP.BAT - Invalid call");
  WScript.Quit(1);

Prueba de arnés y demostración de uso

@echo off
setlocal enableDelayedExpansion
set runs=10
for %%t in (20 30 40 50 70 100 250 500 1000) do (

  (
  set t0=!time!
  for /l %%p in (1,1,%runs%) do call sleep %%t !time!
  set t1=!time!
  )

  for /f "tokens=1-8 delims=:.," %%a in ("!t0: =0!:!t1: =0!") do (
    set /a "a=(((1%%e-1%%a)*60)+1%%f-1%%b)*6000+1%%g%%h-1%%c%%d, a+=(a>>31) & 8640000"
  )

  set /a average_time=a*10/runs
  echo(Input:%%t ms - Output: Average time !average_time! ms
)

- Salida de muestra -

Input:20 ms - Output: Average time 56 ms
Input:30 ms - Output: Average time 55 ms
Input:40 ms - Output: Average time 54 ms
Input:50 ms - Output: Average time 56 ms
Input:70 ms - Output: Average time 71 ms
Input:100 ms - Output: Average time 100 ms
Input:250 ms - Output: Average time 253 ms
Input:500 ms - Output: Average time 501 ms
Input:1000 ms - Output: Average time 1001 ms

LocaleSleep.bat: JScript híbrido / Lote (dependiente de la configuración regional)

Esto es casi idéntico a SLEEP.BAT, excepto que pasa en "%date% %time%" en lugar de "%time%. La ventaja es que ya no tiene problemas con el cambio entre el horario estándar y el horario de verano. La principal desventaja es que depende de la ubicación. Solo funciona si %date% se analiza correctamente mediante el método parse () del objeto Date. Sé que esto debería funcionar en la mayoría de las máquinas de EE. UU., Además de en algunas otras. Se podrían escribir variantes para admitir otras configuraciones regionales, pero cada una dependería de la configuración regional.

LocaleSleep.bat

@if (@X)==(@Y) @end /* harmless hybrid line that begins a JScript comment
@goto :batch
:::
:::LocaleSleep.bat  msec  ["%date% %time%"]
:::LocaleSleep.bat  /?
:::
:::  Suspend processing for msec milliseconds. The optional "%date% %time%"
:::  argument can be added to improve timing accuracy. If called within a
:::  FOR loop, then "!date! !time!" should be used instead, after enabling
:::  delayed expansion.
:::
:::  This utility is locale specific. It only works properly if %date% is in
:::  a format that is parsed properly by the Date object parse() method.
:::
:::  There is a startup time for SLEEP.BAT that limits the shortest delay
:::  possible. The startup time varies from machine to machine. Delays longer
:::  than the minimum are usually accurate to within 10 msec if the
:::  "%date% %time%" argument is provided.
:::
:::  A single /? argument displays this help.
:::
:::  LocaleSleep.bat Version 1.0 - written by Dave Benham
:::

============= :Batch portion ===========
@echo off
if "%~1" equ "/?" (
  for /f "tokens=* delims=:" %%A in ('findstr "^:::" "%~f0"') do @echo(%%A
  exit /b 0
) else cscript //E:JScript //nologo "%~f0" %* "%date% %time%"
exit /b

============ JScript portion ==========*/
try 
  var arg2 = WScript.Arguments.Item(1);
  var delay = Number(WScript.Arguments.Item(0)) - ((new Date())-(new Date(arg2.slice(0,-3))).setMilliseconds( Number(arg2.slice(-2))*10 ));
  if (delay>0) WScript.sleep(delay);
  WScript.Quit(0);
 catch(e) 
  WScript.Stderr.WriteLine("localeSleep.bat - Invalid call");
  WScript.Quit(1);

Prueba de arnés y demostración de uso

@echo off
setlocal enableDelayedExpansion
set runs=10
for %%t in (20 30 40 50 70 100 250 500 1000) do (

  (
  set t0=!time!
  for /l %%p in (1,1,%runs%) do call localeSleep %%t "!date! !time!"
  set t1=!time!
  )

  for /f "tokens=1-8 delims=:.," %%a in ("!t0: =0!:!t1: =0!") do (
    set /a "a=(((1%%e-1%%a)*60)+1%%f-1%%b)*6000+1%%g%%h-1%%c%%d, a+=(a>>31) & 8640000"
  )

  set /a average_time=a*10/runs
  echo(Input:%%t ms - Output: Average time !average_time! ms
)

Los resultados son prácticamente idénticos a SLEEP.BAT

valoraciones y comentarios

Si te ha sido de provecho nuestro artículo, agradeceríamos que lo compartas con otros seniors y nos ayudes a difundir nuestro contenido.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)


Tags : /

Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *