0

I have a script that runs through some files and copies them to another location. But the script needs to wait until the file is no longer being written to. I tried all the solutions here:

But the problem is that they don't work when wrapped in a loop. It always says the file is locked. If the script it cancelled and re-run it correctly finds the file unlocked. Am I doing something wrong or is there a trick to make this work?

For locking a test file, checkfile.txt, I do:

(
  >&2 pause
) >> checkfile.txt

Then the example script to check the file is this:

@echo off


for  %%i in (*.txt) do (

:loop
ping localhost -n 5 > nul
echo "check if locked"
powershell -Command "$FileStream = [System.IO.File]::Open('%%i', 'Open', 'Write'); $FileStream.Close(); $FileStream.Dispose()" >NUL 2>NUL || (goto :loop)

echo "NOT locked anymore"
)

Jan Stanstrup
  • 1,152
  • 11
  • 28

1 Answers1

1

You cannot goto in a loop as it will simply break the for loop entirely. Additionally, the exit code or errorlevel is set for the last successful command. In this case being the powershell dispose command. Simply do the loop outside of the code block:

@echo off & setlocal

for %%i in (*.txt) do call :loop %%~i
goto :EOF

:loop
powershell -Command "[System.IO.File]::Open('%1', 'Open', 'Write')">nul 2>&1 && echo %1 not locked || (
    echo %1 Locked
    ("%systemroot%\system32\timeout.exe" /t 3)>nul
    goto :loop
)

Note, the conditional operators (and &&) and (or ||) helps to evaluate the exit code without needing to do if and else statements.

Edit:

@echo off

for %%i in (*.txt) do (
    setlocal enabledelayedexpansion

    powershell -Command "[System.IO.File]::Open('%%~i', 'Open', 'Write')">nul 2>&1 && echo %%~i not locked || (
        echo %%~i Locked
        ("%systemroot%\system32\timeout.exe" /t 3)>nul
    endlocal
 )
)
Gerhard
  • 22,678
  • 7
  • 27
  • 43
  • Thanks! Sorry for the late reply but it took a while for me to get back to this. This solved my problem after I figured out how to modify it for my real world problem. A few notes for others and my own understanding: 1) on my machine "timeout" is a different program that doesn't have that /t switch. I used "ping" to wait. 2) In my real case the call goes in a nested loop. Then "goto :EOF" needs to be outside all the nested loops and right before the loop function definition which is also kept outside all the loops. Otherwise the loop function is executed indefinitely without any path as input. – Jan Stanstrup May 06 '22 at 10:37
  • Is there a way to not have it keep waiting for the locked file but instead continue to the next file? – Jan Stanstrup May 06 '22 at 10:54
  • Well, yes, but not as you think it might. You will need to add all the files you want to check and let it loop through them all, if it gets to the end, it will start with the first one again. That is basically what I have done in this example where we simply test each of the files in a loop. – Gerhard May 06 '22 at 10:57
  • Then on your first comment, are you saying you do not have `timeout` on your OS? Which Windows version is it that you are running? Timeout exists from about Vista (IIRC). The way this is setup, the `goto :EOF` is just to skip the `:loop` label section once the loop `for` loop is done, else it will fall through into that loop with no value and create an error. – Gerhard May 06 '22 at 11:00
  • I think it is more a case of a "timeout" from GNU coreutils being before on the path. It calls /usr/bin/timeout. "C:\Windows\System32\timeout.exe" would indeed be the one needed for your example. – Jan Stanstrup May 06 '22 at 11:08
  • Yes. So in most cases when we script where multiple possible commands exist, we use the full path to the executables. I will update that in my answer aswell. – Gerhard May 06 '22 at 11:11
  • This seems to fail for filenames that contain an "%". How/where do I escape that? I have "setlocal enabledelayedexpansion" in the real script if that makes a difference – Jan Stanstrup Apr 21 '23 at 13:04
  • 1
    Though it is not disallowed, @JanStanstrup, generally having `%` in a filename can result in scripting issues, similarly to `!` when `delayedexpansion` is enabled. Regardless, see edit. – Gerhard Apr 23 '23 at 08:34
  • please note the `endlocal` I missed in the edit (added now) – Gerhard Apr 24 '23 at 11:09