How to forward output of a command as function argument - batch-file

I need to forward the output of the copy command to WRITELOG function. is there anyway to do this? This is what I tired and not works
copy /y %source% %destination%>WRITELOG
:WRITELOG
if not "%1"=="" (
echo %time% : %*>>%LOGFILE%
)

The suggestion by Stephan would be coded for example as:
#echo off
setlocal EnableDelayedExpansion
set "source=%~1"
set "destination=%~2"
set "LOGFILE=%TEMP%\CopyLog.txt"
for /F "delims=" %%L in ('copy /Y "%source%" "%destination%" 2^>nul') do (
set "CopyOutput=%%L"
if not "!CopyOutput:copied=!" == "%%L" echo %time% : %*>>%LOGFILE%
)
endlocal
For an explanation on how the used commands work, open a command prompt window, run there following commands and read help output for each command:
copy /?
for /?
if /?
set /?
However, this solution is not good because of following reasons:
String copied in IF condition depends on language of command copy.
If the file(s) in source are found, but could not be copied to destination because destination is write-protected, the output of copy is 0 file(s) copied. which is interpreted by this code as successful copy.
The second disadvantage could be avoided by using:
#echo off
setlocal EnableDelayedExpansion
set "source=%~1"
set "destination=%~2"
set "LOGFILE=%TEMP%\CopyLog.txt"
for /F "tokens=1,3" %%A in ('copy /Y "%source%" "%destination%" 2^>nul') do (
echo %%A %%B
if not "%%A" == "0" (
if "%%B" == "copied." echo %time% : %*>>%LOGFILE%
)
)
endlocal
But much better would be evaluating exit code of command copy which is 0 on success and greater 0 on error.
#echo off
setlocal
set "source=%~1"
set "destination=%~2"
set "LOGFILE=%TEMP%\CopyLog.txt"
copy /Y "%source%" "%destination%" >nul 2>nul
if not errorlevel 1 echo %time% : %*>>%LOGFILE%
endlocal
See the Microsoft support article Testing for a Specific Error Level in Batch Files.
if not errorlevel 1 means if exit code of previous command is NOT greater or equal 1 then ...
Or in other words if exit code of previous command is smaller than 1 then ...
Command copy never exits with a negative value like all internal commands of cmd.exe and nearly all console applications because 0 is standard for success and a value greater 0 is an indication for an error.

Related

Log contents of text file before deleting

My script isn't logging the contents of run.txt to log.txt
I've tried to remove the delete command to see if it was deleting it too quickly and therefore couldn't log. But that wasn't the case.
What should I change?
#ECHO OFF &setlocal
SET File=run.txt
type %File%
for /f "tokens=*" %%A in (%File%) do (echo >> log.txt)
del "%File%" /s /f /q > nul
pause
Here is a very simple way to do the task you are requiring.
#echo off
REM Will only grab the first line of the file
set /p file=<run.txt
REM For the last line use a for loop
for /f "tokens=*" %%a in (%file%) do set last_line=%%a
(
echo %file%
)>>log.txt
del /f %file% >nul
If not %errorlevel% equ 0 (
ECHO ERROR - %errorlevel%
pause
exit /b
)
ECHO Success!
timeout /t 003
exit /b %errorlevel%
EXPLANATION
set /p is for set prompt. For more information you can use set /? in your CMD window or check out this site.
I wish I could speak more on what < does, but what it is doing here is piping the content of run.txt to our variable.
Then we echo out our variable to our log file with (ECHO This is our %file%)>>destination
>> is to append where > is to overwrite the file.
(
echo %file%
echo.
)>>%file%
Checking for an error is probably unnecessary, but I believe it is a good habit to build on which is what I am trying to do with that If not %errorlevel% statement.
No error? We Success and timeout ourselves for xxx seconds.
Try:
#ECHO OFF &setlocal
SET "File=run.txt"
type "%File%" >> "log.txt" && (del "%File%" /f > nul)
pause

check for spaces in file name inside of for loop in batch file

I have a batch script that is calling a VBscript file. It reiterates through all files in a watched folder.
It needs to verify if the file name has spaces in it and if so reject the file and not process it with the VBS.
I must have an error on the code as I get the error message:
ELSE was not expected at this time.
I have looked at the documentation and searched for the answer for quite some time, including this question: check "IF" condition inside FOR loop (batch/cmd)
But still, I can't see what is wrong in my syntax:
#ECHO OFF
setlocal ENABLEDELAYEDEXPANSION
call :ReadIni Infolder inFolder
call :ReadIni Outfolder outFolder
echo %inFolder%
echo %outFolder%
pause
:StartLoop
FOR %%I in (%inFolder%\*.srt) DO (
ECHO %%I
ECHO %%~nxI
SET TESTNAME=%%~nxI
ECHO !TESTNAME!
ECHO !TESTNAME: =_!
PAUSE
IF NOT !TESTNAME!==!TESTNAME: =_! (
move "%~sdp0%%~nxI" "%outFolder%\ERROR_IN_FILE_NAME_%%~nxI"
) ELSE (
copy /y "%%I" "%~sdp0%%~nxI"
%~sdp0SRToffset.vbs "%~sdp0%%~nxI" "%~sdp0%%~nxI"
IF %ERRORLEVEL%==1 (
Goto StartLoop
) else (
move "%~sdp0%%~nxI" "%outFolder%\"
move "%~sdp0QC_%%~nxI" "%outFolder%\"
del "%%I"
)
)
)
timeout /t 1
goto StartLoop
:ReadIni
FOR /F "tokens=2 delims==" %%a in ('find "%~1=" config.ini') do set %~2=%%a
exit /b
Any help would be appreciated.
IF NOT "!TESTNAME!"=="!TESTNAME: =_!" (
...
IF %ERRORLEVEL%==1 (
Quoting the strings causes cmd to regard the string as a single entity.
Note that the following if %errorlevel% will be executed using the value of errorlevel at :startloop. (See delayed expansion for reasoning.)
Cure by using if !errorlevel!==1 (. (Using the runtime value of errorlevel as established by the vbs routine.)

Delayed expansion and exclamation marks in strings

Ok, so I'm still pretty new to batch scripting and I have this problem with my code that I'm using setlocal enabledelayedexpansion in my code for the for loop, the For loop goes through folder names in a specific directory, but the problem is that some of the names may include "!"(exclamation marks) and if that is the case they are not counted for in the "!filename!" and when the code creates a new directory it does not include the "!". Also when the xcopy tries to copy the files from the original folder, the folder is not found, because the variable "!filename!" is not the same as the original(does not have the exclamation point).
So I found that for this I need to only add "setlocal enable delayed expansion" to some parts of the code and turn it off at other, but I cant seem to find the right places.
The code:
#ECHO OFF
setlocal enabledelayedexpansion
SET Location_Folder=V:
SET Destination_folder=V:\Nonimportable
SET Check_file_validation=V:\Nonimportable\result.txt
SET Excluded_folder=Nonimportable
set "lineNr=12"
For /f "tokens=*" %%O in ('dir /b /a:d "%Location_Folder%"') do (
set filename=%%O
call D:\somefolder\otherfolder\batscriptname.bat !filename!
set Validation=
echo !filename!| FINDSTR /i /c:"%Excluded_folder%" >NUL
IF ERRORLEVEL 1 (
for /F "skip=12 delims=" %%a in (%Check_file_validation%) do if not defined Validation (
set Validation=%%a
call :valid
)
) else (
echo This folder name is excluded: !filename!
)
)
goto Finish
:valid
echo !Validation!| FINDSTR /c:"1" >NUL
if ERRORLEVEL 1 (
set Folder_path=%Location_Folder%\!filename!
set New_Folder_path=%Destination_folder%\!filename!
mkdir "!New_Folder_path!"
echo D | xcopy /o /y /q /s /v "!Folder_path!" "!New_Folder_path!"
rmdir /s /q "!Folder_path!"
) else (
echo Folder is valid !filename!
goto Finish
)
:Finish
exit /b
The Call part calls another small (~5lines) batch file that checks the sqlplus server if the "!filename!" is valid
EDIT: The whole code works fine and does what it should, unless there is a "!" in the name of some folder.
The problem is the parameter expansion in set filename=%%O.
In %%O is still the exclamation mark, but when delayed expansion is enabled, the bangs are dropped.
The conclusion is simple, delayed expansion have to be disabled when you expand a FOR parameter.
But when you also need delayed expansion?
You simply toggle the mode.
setlocal DisableDelayedExpansion
For /f "tokens=*" %%O in ('dir /b /a:d "%Location_Folder%"') do (
set "filename=%%O" -- Here the DelayedExpansion have to be disabled
setlocal EnableDelayedExpansion
call D:\somefolder\otherfolder\batscriptname.bat filename
set "Validation="
...
endlocal
)
See also my modification of the CALL myBat.bat filename instead of CALL myBat.bat !filename!.
You shouldn't use content with CALL, better use a variable by reference and in your function take the content by
set "_filename=!%1!"
It's because CALL itself has some nasty behaviour with spaces, carets, etc
If you use a variable within a code block (parenthesised series of commands) then %var% will yield the value of the variable when the block is originally encountered (ie parse-time value) and !var! the value of the variable as it changes during the block (ie "run-time" value).
If you call a procedure - internal or external, then the values of the variables that the procedure sees are the run-time values from the parent. If these values are changed within the called procedure then the same rules apply, and the changed values are returned to the parent procedure.
However if you invoke setlocal then any value-variation made is reverted to its original value if you execute an endlocal instruction or reach end-of-file within the context of the setlocal.
OK - so that's how delayedexpansion works.
In your case, there is no need for delayedexpansion at all. In the loop in the mainline (%%O) you can use %%O in place of !filename!. In the :valid procedure, you can move the two set commands outside of the code block and then there's no need at all to use !vars! since no access is required to variables whose values change within blocks.
#ECHO OFF
setlocal
SET Location_Folder=V:
SET Destination_folder=V:\Nonimportable
SET Check_file_validation=V:\Nonimportable\result.txt
SET Excluded_folder=Nonimportable
set "lineNr=12"
For /f "tokens=*" %%O in ('dir /b /a:d "%Location_Folder%"') do (
set filename=%%O
call D:\somefolder\otherfolder\batscriptname.bat %%O
set Validation=
echo %%O| FINDSTR /i /c:"%Excluded_folder%" >NUL
IF ERRORLEVEL 1 (
for /F "skip=12 delims=" %%a in (%Check_file_validation%) do if not defined Validation (
set Validation=%%a
call :valid
)
) else (
echo This folder name is excluded: %%O
)
)
goto Finish
:valid
set Folder_path=%Location_Folder%\%filename%
set New_Folder_path=%Destination_folder%\%filename%
echo %Validation%| FINDSTR /c:"1" >NUL
if ERRORLEVEL 1 (
mkdir "%New_Folder_path%"
echo D | xcopy /o /y /q /s /v "%Folder_path%" "%New_Folder_path%"
rmdir /s /q "%Folder_path%"
) else (
echo Folder is valid %filename%
rem redundant instruction : goto Finish
)
:Finish
exit /b

Batch file to count files matching first 6 characters in file name and output file group and its count

I would like to have a batch file to count all file names with common prefix and output the file group and its count. I have these files in a directory:
A1110601.zip
A1110602.zip
A1110603.zip
A1120601.zip
A1120602.zip
I want to group the first 3 by A11106*.zip and the last two by A11206*.zip.
My desired output is:
A11106: 3
A11206: 2
I have tried to copy sample codes from the forum, but they don't fulfilled my desired output.
Here is the code I have so far. But the output is not as described above.
#echo off
title Store Data Counter
:recurse
set I=1
echo "files counter"
FOR /f "tokens=*" %%A IN ('dir /a-d /b "Z:\StoreData\A11106*.zip"') do (call :showfiles "%%A")
echo A111: %I%
FOR /f "tokens=1" %%A IN ('dir /a-d /b "Z:\StoreData\A11206*.zip"') do (call :showfiles "%%A")
echo A112: %I%
pause
goto :eof
:showfiles
echo %1
set /a I+=1
goto :eof
The following batch script should do what you want -- let us call it mask-count.bat:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
:LOOP
set "MASK=%~1"
if defined MASK (
call :SUB "%~1"
shift /1
goto :LOOP
)
endlocal
exit /B
:SUB
set "ARG=%~1"
setlocal EnableDelayedExpansion
set "NUMF=0"
for /F "skip=4 tokens=1" %%L in ('
2^> nul dir /A:-D /N /-C "!ARG!"
') do (
set "NUMF=!NUMD!"
set "NUMD=%%L"
)
echo(!ARG!: %NUMF%
endlocal
exit /B
To use this script, provide the applicable file masks as command line arguments; for instance:
mask-count.bat "A11106*.zip" "A11206*.zip"
This would lead to the following output when applied to your directory:
A11106*.zip: 3
A11206*.zip: 2
Here is a batch code for this task.
#echo off
setlocal EnableExtensions
for %%I in ("*.zip") do call :CountFile "%%~nI"
for /F "tokens=2,3 delims=#=" %%I in ('set Group# 2^>nul') do echo %%I: %%J
endlocal
goto :EOF
:CountFile
set "FileName=%~1"
set "FileGroup=%FileName:~0,6%"
if "Group#%FileGroup%" == "" (
set "Group#%FileGroup%=1"
) else (
set /A Group#%FileGroup%+=1
)
goto :EOF
"*.zip" can be extended with full path to the ZIP files to count.
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
call /?
echo /?
endlocal /?
for /?
goto /?
if /?
set /?
setlocal /?
And read also the Microsoft article about Using command redirection operators for an explanation of 2>nul used here to redirect the error message output by command SET from STDERR to device NUL to suppress it in case of no *.zip file could be found at all.
The redirection operator > must be escaped here with ^ to be interpreted on executing command SET and not on parsing the FOR command line. Otherwise 2>nul without ^ would be interpreted as redirection for command FOR defined at an invalid position in the command line which would result in a batch execution exit because of a syntax error.

Compare Size of Same File in Batch File

I have 1 task where there is 1 file start generating through DB and took almost 1 hour to generate, there is 1 batch file which check this file whenever it is available it calls a new batch file and trigger "file sent", the issue is that this file which is generating continuously did not completely generated at the file picked the same and call the batch file
What i want to do to build a logic where i can compare the size of file within a loop with 2 variable sizeA and sizeB, and call another batch file when sizeA==sizeB now the only issue is i am not sure how to built this logic in a code.\
Here is what I have tried:
#echo off
setlocal EnableDelayedExpansion
set file = "C:\Users\rb54761\Desktop\New folder\File.txt"
set "size=0"
pause
:loop for /f "tokens=*" %%x in ('dir /s /a /b "%file%"2^>nul') do set /a size=%%~zx
echo !size!
PAUSE
if !size! == !size! goto call
goto loop
:call echo Success
EDIT I only saw your comment now for being a 2gb file, then the below code will not work.
If the file were to be smaller..here is an example:
#echo off
set "myfile=C:\Users\rb54761\Desktop\New folder\File.txt"
:start
for /f %%I in ("%myfile%") do set size=%%~zI
if %size% LSS 100 (echo file not ready %size% & timeout /t 10 & goto :start)
echo copy file.
as per above, It will check the file for a size of 100kb, if not that size yet, it will timeout for 10 seconds and goto the beginning and test again until the file reaches 100kb, where it will no longer meet the if statement and pass that line and simply echo copy file.
Please note there are no spaces in my set commands. I would suggest you run from cmd the help for /? for more on this command.
We can use below approach in order to compare the size of same file within a span of time
#echo off
setlocal EnableDelayedExpansion
set file="d:\File_Mask1\New_File\File.txt"
set "size=0"
If exist %file% GOTO loop
GOTO nofile
:loop
for /f "tokens=*" %%x in ('dir /s /a /b %file%') do set /a size=%%~zx
echo !size!
waitfor SomethingThatIsNeverHappening /T 120 >nul 2>&1
for /f "tokens=*" %%y in ('dir /s /a /b %file%') do set /a size1=%%~zy
echo !size1!
if !size! EQU !size1! goto call
goto loop
:call
echo Success
endlocal
This takes advantage of the archive attribute. Windows sets this attribute each time it writes to the file.
So if we can unset it, wait for a time and check it (I use dir /aa which will not find the file, when the attribute is not set)
#echo off
set sec=10
set "file="C:\Users\rb54761\Desktop\New folder\File.txt""
set /a secs=sec+1
:loop
attrib -a "%file%"
timeout %sec% >nul
dir /b /aa "%file%" >nul 2>&1 || goto :loop
echo %file% didn't change since %secs% seconds
(Note: your set file = ... line is wrong. The spaces around the = will become part of the variable name respective the value)
Advantage: no file size limit (files bigger 2GB will be handled fine too)

Resources