I am developing a batch file to collect websphere products information, it seems to work fine except for some cases.
For some reason under some circumstances versionInfo.bat -maintenancePackages is called but the following code (check for manageprofiles.bat), it seems like it's returning from the :check section after calling versionInfo.
My Windows batch writing skills are very rusty, other improvements are welcome.
#echo off
SetLocal EnableDelayedExpansion
set tmpfile=%TEMP%\tmpdone.txt
echo. > %tmpfile%
For /F "eol= delims=| tokens=13" %%a in (%windir%\vpd.properties) Do (
set check=%%a
call :check
)
goto eof
:check
Set skip=No
For /F "eol= delims=|" %%a in (%tmpfile%) Do (
if "%%a" == "%check%" set skip=YES
)
if %skip% == YES goto eof
echo %check%>>%tmpfile%
if exist "%check%\bin\versionInfo.bat" "%check%\bin\versionInfo.bat" -maintenancePackages
echo %check%\bin\manageprofiles.bat
if exist "%check%\bin\manageprofiles.bat" "%check%\bin\manageprofiles.bat" -listProfiles
goto eof
:del
echo Done
del %tmpfile%
:eof
You need to use call to run batch files from another batch file. Otherwise cmd won't return from the called one. So your code should read:
if exist "%check%\bin\versionInfo.bat" call "%check%\bin\versionInfo.bat" -maintenancePackages
echo %check%\bin\manageprofiles.bat
if exist "%check%\bin\manageprofiles.bat" call "%check%\bin\manageprofiles.bat" -listProfiles
goto :eof
(Also no need for a :eof jump label, you can just use the goto :eof special syntax to exit the batch file directly. I usually only use such a jump label if I need some cleanup to do first, but I name it differently then, to avoid confusion :-))
Related
I set up a batch (copy.bat) file in my Windows sendto Directory that is for Copying a bunch of files via rightclicking the directories and choosing sendto "copy.bat".
#echo off
setlocal enabledelayedexpansion
set cnt=0
set cur=0
:files
for /R "%~1" %%F in (*.7z, *.avi) do (set /a cnt+=1)
shift
if "%~1" NEQ "" goto :files
echo !cnt! files found for copying.
echo Processing ...
set "DEST_DIR=E:\"
:while
for /R "%~1" %%F IN (*.7z, *.avi) do (
if exist "%%F" (
set FILE_DIR=%%~dpF
set FILE_INTERMEDIATE_DIR=!%%~dpF:%%~1%=!
xcopy /E /I /Y "%%F" "%DEST_DIR%!FILE_INTERMEDIATE_DIR!"
)
set /a cur+=1
echo !cur!/!cnt! done...
)
shift
if "%~1" NEQ "" goto :while
pause
If I run the first for loop alone it counts the files.
If I run the second loop on its own it copies exatly those files the other loop is supposed to count.
However, both loops in one batch file will somehowe conflict. The Filecount works correctly but the copy process delivers an error: "The Path of is too long". Between "of" and "is" there is an additional Space. The final pause will correctly wait for button press. I assume it has to do something with the %%F variable, but renaming it in one of the loops does not help.
Please change the name of your .bat - copy is a keyword to cmd.
Your problem is that the first routine shifts out all of the parameters, so your second routine has no parameters to deal with.
The easiest way is probably:
set "DEST_DIR=E:\"
call :while %*
pause
goto :eof
:while
for /R "%~1" %%F IN (*.7z, *.avi) do (
...
if "%~1" NEQ "" goto while
goto :eof
which executes the second routine as en internal subroutine (the : in the call is required) providing that routine with a fresh copy of the parameter-list (%*)
You could also consider moving the if "~1"... to the start of the routine (just after the :while), change it to if "%~1" EQU "" goto :eof and change the final statement to goto while.
The label :eof is defined as end-of-file by cmd and should not be declared.
I am trying to write a batch script that will loop through a list of file names and:
Attempt to create that file
Check if the creation was successful, and if not, try again after a few seconds
This is as far as I've gotten, which borrows from this question. However, it will only check and create file1.txt, not file2.txt or file3.txt. Then it will create a file "%A". Why is it only looping through the first file, and where is "%A" coming from?
I assume it is something to do with how I'm using the variable %A. Especially because if I manually type this out in the command prompt, instead of a batch file, and using %A instead of %%A, it works correctly.
FOR %%A IN ("file1.txt" "file2.txt" "file3txt") DO (
:Check
IF EXIST %%A GOTO Found
ECHO NotFound: %%A
echo.>%%A
timeout /t 5
GOTO Check
:Found
ECHO Found: %%A
)
You can accomplish this by using the CALL command to essentially work like a Function. When you use CALL the code execution will return to the original spot in the code it was called from and continue on.
#echo off
FOR %%A IN ("file1.txt" "file2.txt" "file3txt") DO (
CALL :CHECK "%%~A"
)
GOTO :EOF
REM Functions only below this line
:Check
IF EXIST "%~1" (
ECHO Found: %~1
) ELSE (
ECHO NotFound: %~1
timeout /t 5
GOTO Check
)
GOTO :EOF
I'm feeling a bit constrained by batch's limitations and unsure the best way to code some logic. I have a hybrid Batch/VBScript for printing and decided the best way to confirm the VBScript had finished was by utilizing an external file as a placeholder.
The logic is like this:
Batch Script --> Create tmpfile --> Run VBScript
VBScript --> Print Job --> Delete File
That part works, but in my "call script" I have an array of files that I use a FOR LOOP to iterate over and print, but I can't figure out how to code the logic WHILE FILE EXISTS DON'T RUN NEXT PRINT JOB. I have figured out how to "loop" until the file doesn't exist, but this uses GoTo statements and they don't work inside FOR LOOPS. I tried using "EXIT /B" which I thought just exits a function, but it just closed the main script all together.
Here is my "call script" which needs some help~
::This is a placeholder file to check if VBScript is running
SET File="%TEMP%\jobrunning.txt"
del /F /Q %File%
::Print Each File in Array
for /l %%n in (0,1,%arrsize%) do (
SET FILEPATH="FILEPATH=!file[%%n]!
GoTo file_exists
)
::Is Job Still Running?
:file_exists
IF EXIST %File% (
GoTo :file_exists
) ELSE (
ECHO %PFUNC% %FILEPATH%
%PFUNC% %FILEPATH%
EXIT /B
)
The issue was using GoTo w/ EXIT /B. GoTo just jumps to a LINELABEL, and EXIT /B exits PROCESS IE: The Script. CALL opens a SUBPROCESS and EXIT /B returns to the main PROCESS allow further code execution. (It's the same as calling an external script and closing it).
This is what I ended up using to test and just deleted the file manually after each iteration of the array.
#ECHO OFF
setlocal enabledelayedexpansion
::This is a placeholder file to check if VBScript is running
SET File="%TEMP%\jobrunning"
del /F /Q %File%
::Set arrsize -1
set arrsize=4
set arrayline[0]=1A
set arrayline[1]=2A
set arrayline[2]=3A
set arrayline[3]=4A
set arrayline[4]=5A
::read it using a FOR /L statement
for /l %%n in (0,1,%arrsize%) do (
copy /y NUL %TEMP%\jobrunning.txt >NUL
TIMEOUT 2
CALL :file_exists
echo !arrayline[%%n]!
)
PAUSE
GoTo :EOF
:Is Job Still Running?
:file_exists
IF EXIST %File% (
GoTo :file_exists
) ELSE (
EXIT /B
)
I'm trying to fill X files with chunks of content from a .txt file.
The thing is the .txt (source) file is small and I want my code to loop over it's content chunk by chunk and start again when no more chunks are avaliable until all files have been filled.
This is part of the (still on development) code. I've managed to teach my Batch.bat to recognized the chunks but I don't know how to check if I am at EOF to go back again to the begining.
Current code doesn't work since it has been simplified for the question (delayed expansion not present)
SET /A J=1
FOR /F "skip=%J% tokens=*" %%A IN (%~1%) DO (
CALL :CSV "%%A" %J%
)
EXIT /B
:CSV
SETLOCAL
IF NOT "%~1"=="---" (
ECHO %~1 >> Chistaco.txt
)
ENDLOCAL && SET /A J=%2+1
Code posted here is just a guide of my target, don't miss the question.
Thank you !
EDIT: Simplified Code Module
#ECHO OFF
SETLOCAL
REM Ask for parameters
SET /P filler=Enter the name of the file to use as filler (file.ext):
SET /P seed=Enter the seed of the files to fill:
CLS && ECHO %~1%
REM The variable "J" counts the lines to jump in "file.ext" (and btw, it cannot be 0 as the batch parses fails to read it)
REM This is where the EOF check is needed: "J" must be referenced outside the main loop so the script can loop inside "file.ext" untill all "seed???" files have been filled
SET /A J=1
FOR /R "%path%" %%X IN (%seed%*) DO ECHO %%X && CALL :FillerChomper "%filler%" "%%X"
ENDLOCAL
EXIT /B
:FillerChomper
SETLOCAL
FOR /F "skip=%J% tokens=*" %%A IN (%~1%) DO (
CALL :CSV "%%A" J %2%
IF "%%A"=="---" EXIT /B
)
EXIT /B
:CSV
SETLOCAL
IF NOT %1=="---" ECHO %~1 >> %~3%
ENDLOCAL && SET /A %2=%J%+1
EXIT /B
REM All functions must have a "EXIT /B" command or batch will just keep executing codelines until :EOF
Thank you guys for your help!
I have been successfully using the CALL mechanism to allow one batch file to CALL another to setup environment variables. This code has been working well for over a year on Windows XP.
However, it does not appear to be working in the same way on Windows 7. The variables exist in the second batch file just before the EXIT /B statement. But, they do not exist upon the return to the first batch file.
Some trivial examples seem to work as expected, but the large batch scripts do not.
Has anyone had difficulties with this or know any workarounds?
In years of advanced batch scripting, I have never seen a CALL fail to preserve environment variables unless the called script (or label) set the variable when SETLOCAL was still active. There is an implicit ENDLOCAL for every active SETLOCAL from the within the CALL upon termination of the CALL.
It sounds like you have put in diagnostic messages prior to your EXIT /B to confirm that your variables are defined. I would take it one step further and add multiple ENDLOCAL statements prior to your diagnostic messages. I suspect you will then see your values dissapear prior to EXIT /B. You can add as many ENDLOCAL as you want. ENDLOCAL will never affect SETLOCAL that occurred prior to the CALL.
The most likely explanation is that either your script has somehow changed from XP to Win 7, or else there is some context change in your Win 7 environment that is exercising some aspect of the code that hadn't been exposed before.
Try this:
(
ENDLOCAL
SET "_Var1=Some Variable You want to exist"
SET "_Var2=Some Other Variable You want to exist"
EXIT /b 0
)
Also make sure you call Batch 2 from batch 1 like this:
CALL "\\PathToBatch2\Batch2.cmd"
ALTERNATELY you can do this:
CMD One:
REM Script: Batch1
#(
SETLOCAL
ECHO OFF
SET "_CallBatch2=C:\PathToBatch2\Batch2.cmd"
SET "_SetCmd=CALL :SetCMD "
SET "_RecievedVarList="
SET "_RecievedVar1=" & REM -- Note only done to show this is being created, normally you won't know or care what variables are being returned.
SET "_eLvL=0"
)
CALL :Main
(
ENDLOCAL
EXIT /b %_eLvl%
)
:Main
FOR %%A IN (CALL "%_CallBatch2%") DO (
IF /I "%%~A" EQU "SET" (
REM CALL %%A "%%~B" would work too
%_SetCmd% %%~B
) ELSE (
REM Looks like this was intended to be some output, show it.
ECHO.%%A %%B
)
)
FOR /F "Tokens=1*" %%A IN (%_RecievedVarList%) DO (
REM ECHO the Variable's name and it's contents:
CALL ECHO."%%~A" = "%%%%~A%%"
)
GOTO :EOF
:SetCMD
SET "%*"
FOR /F "Tokens=1 Delims==" %A IN ("%*") DO (
REM Store vars to output later to check their values.
SET "_RecievedVarList=%_RecievedVarList% "%A""
)
GOTO :EOF
.
CMD Two:
REM Script: Batch2
#(
SETLOCAL
ECHO OFF
)
CALL :Main
(
ENDLOCAL
EXIT /b %_eLvl%
)
:Main
ECHO.SET "_RecievedVar1=This is Recieved Var 1"
ECHO.SET "_RecievedVar2=This is Recieved Var 2"
GOTO :EOF