es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

Powershell no lee correctamente el tamaño de los archivos si estos están en proceso de cambio (es decir, si se están descargando los archivos).

Estoy creando un script de Powershell que tiene como objetivo apagar una máquina de Windows (portátil/PC) una vez que se haya completado la descarga de uno o más archivos (por ejemplo, asumamos que aquí se trata de un solo archivo grande).

En pocas palabras, estamos leyendo el tamaño de la carpeta de descargas completa cada x segundos y comparamos los tamaños antes y después del retardo. Si no hay cambios en el tiempo reciente, eso significa que la descarga está completada o bloqueada, ambos casos llevan al apagado.

Dado el siguiente script (el obtentor de tamaño puede ser una función independiente en algún momento, sí):

#Powershell5
#Mayor  Menor  Compilación  Revisión
# -----  -----  ------------  --------
# 5      1      19041        1320  

$downloadDir = "C:\Users\edi\Downloads"

while (1) 
{
    $s1 = Get-ChildItem -Path $downloadDir | Measure-Object -Property Length -Sum | Select-Object Sum
    write-host "S1:" $s1.sum

    # este tiempo de 3 segundos es solo para fines de prueba; en un escenario real probablemente se establecerá en 60 o 120 segundos.
    Start-Sleep -s 3

    $s2 = Get-ChildItem -Path $downloadDir | Measure-Object -Property Length -Sum | Select-Object Sum
    write-host "S2:" $s2.sum

    if ($s1.sum -eq $s2.sum) 
    {
        write-host "Descarga completada, apagando..."
        # la salida del bucle es el apagado real; comentado para fines de prueba.
        # shutdown /s
    } 
}

El problema al que me enfrento es que la lectura de los tamaños de archivo no se realiza “en tiempo real”. En otras palabras, el tamaño del archivo no cambia como normalmente se vería en la vista del Explorador. Necesito poder leer estos números (cambiando el tamaño del archivo) en tiempo real.

Dato interesante: mientras la descarga está en progreso y se ejecuta el script, si se va manualmente a la carpeta de descargas y se presiona F5 / Actualizar… los números cambian (la lectura del tamaño es precisa).

Nota: Mi investigación me llevó a este artículo que podría presentar la causa raíz, pero no estoy 100% seguro de ello: https://devblogs.microsoft.com/oldnewthing/20111226-00/?p=8813

Agradezco cualquier idea al respecto. ¡Gracias de antemano!

Tags:  , ,

Answer

  1. Avatar for davy.ai

    Solución para leer tamaños de archivo en tiempo real utilizando Powershell

    El problema con el script dado es que la lectura de los tamaños de archivo no se realiza en tiempo real. En otras palabras, el tamaño del archivo no cambia como se vería normalmente en la vista de Explorador. Sin embargo, podemos modificar el script para que lea los tamaños de archivo en tiempo real utilizando la clase FileSystemWatcher.

    La clase FileSystemWatcher es parte del espacio de nombres System.IO y nos permite monitorear el sistema de archivos y responder a cambios en archivos y directorios. En nuestro caso, podemos crear una instancia de FileSystemWatcher y establecer su propiedad Path en el directorio de descargas. Luego, podemos suscribirnos al evento Changed de la instancia de FileSystemWatcher y llamar a los cmdlets Get-ChildItem y Measure-Object para obtener el tamaño actual del directorio de descargas cada vez que se active el evento.

    Aquí hay un ejemplo de cómo podemos modificar el script para usar FileSystemWatcher:

    #Powershell5
    #Principal  Secundaria  Compilación  Revisión
    # -----  -----  -----  --------
    # 5      1      19041  1320  
    
    $downloadDir = "C:\Users\edi\Downloads"
    
    $watcher = New-Object System.IO.FileSystemWatcher
    $watcher.Path = $downloadDir
    $watcher.IncludeSubdirectories = $false
    $watcher.EnableRaisingEvents = $true
    
    $completed = $false
    
    Register-ObjectEvent $watcher Changed -SourceIdentifier Watcher -Action {
        $s2 = Get-ChildItem -Path $downloadDir | Measure-Object -Property Length -Sum | Select-Object Sum
        write-host "S2:" $s2.sum
        if ($s1 -ne $s2.sum) {
            $s1 = $s2.sum
            $completed = $false
        }
        elseif (-not $completed) {
            $completed = $true
            write-host "Descarga completa, apagando..."
            # La salida del bucle es el apagado real; comentado para fines de prueba.
            #shutdown /s
        }
    }
    
    $s1 = Get-ChildItem -Path $downloadDir | Measure-Object -Property Length -Sum | Select-Object Sum
    write-host "S1:" $s1.sum
    
    while (-not $completed) {
        Start-Sleep -Seconds 1
    }
    

    En este script modificado, creamos una instancia de FileSystemWatcher y establecemos su propiedad Path en el directorio de descargas. También configuramos IncludeSubdirectories en $false para monitorear solo el directorio de descargas, y EnableRaisingEvents en $true para comenzar a generar eventos.

    Luego, registramos un controlador de eventos para el evento Changed utilizando el cmdlet Register-ObjectEvent. En el controlador de eventos, obtenemos el tamaño actual del directorio de descargas y lo comparamos con el tamaño anterior. Si los tamaños son diferentes, actualizamos el tamaño anterior y establecemos la bandera completed en $false. Si los tamaños son iguales y la bandera completed es $false, establecemos la bandera completed en $true y escribimos un mensaje en la consola.

    Finalmente, obtenemos el tamaño inicial del directorio de descargas y entramos en un bucle que espera a que la bandera completed se establezca en $true. Utilizamos un intervalo de espera de 1 segundo para evitar un uso excesivo de la CPU.

    Este script modificado debería leer los tamaños de archivo en tiempo real y detectar correctamente cuando se completa la descarga.

Comments are closed.