Variable not being set in Visual Studio Post-build event command line - windows

I have the following script in a Windows batch file that fetches the version of a DLL file and renames an existing file by inserting the version in its file name. This works fine when I put it in a .bat file and run it in Windows. The resultant file name is GCSv1.1.0.7316.zip.
SET WMICommand="WMIC DATAFILE WHERE name='E:\\Projects\\GCS\\bin\\Debug\\Client.dll' get Version /value"
for /f "tokens=2 delims==" %%x in ('%WMICommand%') do (set vers=%%x)
echo %vers%
echo GCS_v%vers%.zip
ren E:\Projects\GCS\InstallerTemplateProject\DeployFolder\GCS_Package.zip GCS_v%vers%.zip
I need to take this same script and put it in a Post-build event command line in Visual Studio to rename the generated file as a last step in the build process. Which is as follows:
SET WMICommand="WMIC DATAFILE WHERE name='$(SolutionDir)GCS\bin\debug\\Client.dll' get Version /value"
for /f "tokens=2 delims==" %%x in ("%WMICommand%") do (set vers=%%x)
ren "$(SolutionDir)GCS\InstallerTemplateProject\DeployFolder\GCS_Package.zip" GCS_v%vers%.zip
But for some reason, the renaming is not happening properly because it is being renamed to GCS_v.zip.
Clearly the %vers% variable is not being set when it is in the Post-build event command line. Why does it work when executing it in a .bat file on Windows, but not when executing in a post-build? What should I do differently to the variable here? The goal is to get the second batch working in visual Studio. The first batch I wrote it just to test it out.

Each of the batchfiles is invoked independently and instantiates its own environment, so the second batch does not acquire the changes made the the first's variables.
Two obvious solutions
Use setx to set the variable into the second batch (with the downside that it will also be included in the environment of any other batch started following the setx) - probably then use setx to clear the variable from the second batch.
Write the variable to a file and read the file back into the second batch.
Ah - my misinterpretation, caused by skimming. I'd assumed the first batch was a pre-build and the second a post-build.
The problem appears to be that VS is not substituting the actual (and no doubt per-project) value of Solutiondir into the batch - which it logically cannot do because there's no guarantee that $(whatever) isn't a string with another interpretation within a random batchfile.
I'd suggest that you try
set>afilename.txt
as the first command within the post-build batch, which will create a file containing the value of most of the environment variables. It may very well be that a variable named something like solutiondir is installed in the environment for the post-build batch - but I can't guarantee that...

Related

Schedule batch file not renaming file to name specified using %date% and %time% variables

As part of a regular file upload process we run a .bat file via Windows Task Scheduler. It opens WinSCPand runs it using a config file.
Then it cds to the file where the upload is stored, renames it, then moves it to the archive file.
If I run the program manually with a pause before the exit, it works fine. Currently is just dumping the file from upload to the archive without renaming it with time and date appended.
#echo off
"C:\Program Files (x86)\WinSCP\winscp.com" /script=CONFIG.txt
cd C:\SCHEDULEDQUERIES\PressGaney\Upload
ren *.csv CL6019_%time:~0,2%%time:~3,2%%date:~-10,2%%date:~3,2%%date:~-4,4%.csv
move *.csv C:\SCHEDULEDQUERIES\PressGaney\archive
exit
Thanks. Happy to give any further details that may be needed.
For lack of further information, I'd suggest
ren *.csv "CL6019_%time:~0,2%%time:~3,2%%date:~-10,2%%date:~3,2%%date:~-4,4%.csv"
should cure the problem. If not, echo this line and then pause the script.
Perhaps your time format - or the time format used by the by the user under which the job is being run by the task scheduler - is set to single-digit hours, which replaces the leading 0 in the time with a space, so the original ren function sees three arguments, not two.
Of course, f you try to debug this during normal working hours, after morning coffee at 10:00 or later, the time won't contain the space, so it seems to work with your tests.
Wrap the batch file to another one and redirect its complete output to a log file:
winscp_script.bat > c:\writable\path\winscp_script.log
Next day, inspect the log file for any errors.
In general, you should not rely on %TIME% and %DATE% variables, as their format is locale specific. The local account that runs your Windows Scheduler task can have a different locale than the one you use to test the batch file. Not only you get a wrong name, but if the resulting format includes spaces, it would completely break the ren command (as already suggested by #Magoo).
WinSCP itself has a built-in feature for time formatting, so you can do something like:
set TIMESTAMP_FORMAT=hhnnddmmyyyy
pushd "C:\Program Files (x86)\WinSCP"
for /F "tokens=* USEBACKQ" %%F in (
`winscp.com /command "echo %%TIMESTAMP#%TIMESTAMP_FORMAT%%%" "exit"`
) do set TIMESTAMP=%%F
popd
echo %TIMESTAMP%
ren *.csv CL6019_%TIMESTAMP%.csv

How to Increment a FileName in batch? (Part2)

I already tried the answer given by Mofi in my last question regarding this topic. But I changed the base name and it does not seem to work by now. If you want to see the previous question:How do I increment a filename in batch? What is wrong with this new code? It does not make a new file it just overwrites the previous made file.
:MainProcessNew
cd /D "%USERPROFILE%\Desktop"
for /F %%G in (*.json) do (
set "FileName=%%G"
set "BaseName=Device"
set "FileNumber=0"
)
:FileNameLoop
set /A FileNumber+=1
if exist "%BaseName%%FileNumber%.json" (
goto FileNameLoop
)
echo.>"%USERPROFILE%\Desktop\%BaseName%%FileNumber%.json"
I'm quite sure the batch code in question is not complete or reduced to a script code not really suitable to reproduce the problem because with Device.json existing in folder Desktop and no other Device*.json file existing, the empty line is first written to Device1.json. A file with name Device.json is never overwritten by the batch code in question because variable FileNumber has always at least the value 1.
Well, the FOR option /F is most likely wrong here as I suppose the FOR loop should search for *.json files as done without /F. Using a wildcard pattern like *.json together with option/F results in error message:
The system cannot find the file *.json.
Run in a command prompt window for /? or help for for help on syntax of this command.
It is completely unclear what is the purpose of the FOR loop because the FileName variable is not used at all. This variable should perhaps hold the name of last found *.json if there was any *.json file found at all. But that also does not make sense if not further used anywhere.
It is also unclear why BaseName and FileNumber are defined inside the loop and not outside.
In the complete batch code the label FileNameLoop is perhaps the beginning of a subroutine. But in the reduced batch code in question there is no call :FileNameLoop "%%G" which I would expect in this case.
So the question is hard to answer as it is unclear what is really the problem with posted batch code.
:MainProcessNew
cd /D "%USERPROFILE%\Desktop"
rem Useless FOR loop without /F commented out.
rem for %%G in (*.json) do set "FileName=%%G"
set "BaseName=Device"
set "FileNumber="
rem Skip searching for files with a file number
rem after Device if there is no Device.json file.
if not exist "%BaseName%.json" goto CreateFile
rem Otherwise keep Device.json as is and search for Device1.json,
rem Device2.json, ... until a Device*.json file with current number
rem is not found and use this number for next Device*.json file.
set "FileNumber=0"
:FileNameLoop
set /A FileNumber+=1
if exist "%BaseName%%FileNumber%.json" goto FileNameLoop
:CreateFile
rem Note: FileNumber is replaced in the line below by an empty
rem string if there is no Device.json in desktop folder.
echo.>"%USERPROFILE%\Desktop\%BaseName%%FileNumber%.json"
Hint: For debugging a batch file
comment out or remove all #echo off or echo off in the batch file or change off to on,
open a command prompt window,
enter "Path to batch file\BachFileName.bat" and press RETURN.
Now it can be seen in the command prompt window each line executed by Windows command processor after preprocessing each line and each command block which means after replacing all %VariableName% by the current value of the variable in current line or entire command block.
And error messages can be also seen as the command prompt window remains open after processing batch file stopped, except it contains the command exit without option /B which always terminates the current command process.

Using a Windows Batch file to back up files in user-specified location and user-specified file extension

I'm creating a Windows Batch file which will allow me to backup files in a specific location with a specific file extension by creating a copy of the file with a .bak extension. For example, if I were to give the working Batch file the parameters "C:\Test & Folder" (which, in this example, only contains a file called MyWork.laz) and ".laz", it would switch to the correct drive, look through the specified folder, create a copy of MyWork.laz in the same folder called MyWork.laz.bak, then switch back to the drive where the batch was started from.
Currently the code looks like this:
set EXTEN = *%~2%
%~d1
cd "%~f1"
FOR %%I IN %%EXTEN DO COPY %%I %%I.bak
%~d0
However, when the batch file gets to Line 4, the following error is outputted:
%EXTEN was unexpected at this time.
How Line 4 is interpreted by Windows is also shown (I was using ".bak" as my second argument):
FOR %I IN %EXTEN DO COPY %I %I.bak
As I'm new to Windows Batch programming, and my research into this hasn't yielded anything, I'd appreciate any advice or assistance.
The simple batch solution:
#ECHO OFF
FOR /F "usebackq delims=" %%I IN ( `DIR /B "%~1*.%~2"` ) DO COPY "%~1%%I" "%~1%%I.bak" >nul
This simple solution runs command DIR searching for files with the extension passed as second parameter in directory specified as first parameter.
Output format of command DIR is set with option /B to simple format which means only name of found files without path are output by DIR.
This output is read next line by line in the FOR loop which executes the copy command on each file to create the backup file in same directory.
But this simple solution has some disadvantages for usage:
There is no check if the batch file was called with the 2 parameters required.
There is no check if the directory path passed as first parameter really ends with a backslash as required.
There is no check if the extension passed as second parameter does not contain a dot as expected.
Therefore the better solution is following batch file:
#ECHO ON
SET "Folder=%~1"
SET "Extension=%~2"
IF "%Folder%"=="" GOTO Usage
IF "%Extension%"=="" GOTO Usage
IF "%Folder:~-1%"=="\" SET "Folder=%Folder:~0,-1%"
IF "%Extension:~0,1%"=="." SET "Extension=%Extension:~1%"
FOR /F "usebackq delims=" %%I IN ( `dir /b "%Folder%\*.%Extension%"` ) DO COPY "%Folder%\%%I" "%Folder%\%%I.bak" >NUL
GOTO EndBatch
:Usage
CLS
ECHO Usage: %~n0 ["][Drive:]Path["] Extension
ECHO.
ECHO Examples:
ECHO.
ECHO %~n0 "C:\My Files\" txt
ECHO.
ECHO %~n0 C:\Temp\ doc
ECHO.
PAUSE
:EndBatch
SET Folder=
SET Extension=
The second line assigns first parameter to an environment variable Folder with removing surrounding double quotes if present at all. The entire parameter for command SET must be in double quotes in case of directory path contains a special character like & or %.
The third line assigns second parameter to an environment variable Extension with removing surrounding double quotes if present at all (unlikely but not impossible).
The fourth and fifth line check if batch file was called with at least 2 parameters.
The check for first parameter missing can be done safely only after removing the double quotes as otherwise the execution of the batch file is breaked with a syntax error message if the directory path contains a special character like &.
IF ""C:\Test & Folder""==""
results in a syntax eror while
IF "C:\Test & Folder"==""
is correct parsed by the command line interpreter.
The sixth line checks if last character of directory path is a backslash. The backslash at end is removed if this is the case. The opposite is also possible by appending a backslash if missing at end. But using 3 times a backslash in the FOR loop below makes this line easier to read.
The seventh line removes a dot from beginning of file extension if the file extension was specified on calling the batch file with a dot.
The other lines should be self-explaining.
Addition to answer the questions by Expack3 in first comment:
Whenever a new process is started, Windows allocates memory for the environment variables and their values, and copies the environment data from the parent process to the environment memory of the called process. Therefore each process has its own environment buffer and can modify, add or delete environment variables with no impact on other running processes including the parent process.
The process cmd.exe is called on running a batch file which interprets and executes the commands in the batch file. By default cmd.exe terminates itself on end of a batch file reached. With termination of cmd.exe the environment variables buffer is also deallocated and therefore it does not really matter which variables have been added, modified or deleted by the batch file. So in general it is not necessary to delete environment variables used temporarily in a batch file.
But instead of double clicking on a batch file, or running a batch file via a shortcut (*.lnk file), or by Windows task scheduler, it is also possible to run a batch file directly from within a command prompt window which means from an already running cmd.exe process.
In this case the environment variables added by the executed batch file remain in environment buffer after processing the batch file finished. This can lead to problems if next from within same command prompt window one more batch file is executed which by chance use also one of the environment variables of first batch file, but has not initialized the environment variable before first usage. The mistake of a missing variable initialization in second batch file in combination of not removing variables in first batch file could result now in a different execution of second batch file in comparison to running only second batch file respectively running the second batch file first in command prompt window.
Another reason for deleting temporarily used environment variables is the limited memory for environment variables. The memory allocated by Windows for the environment variables before starting the process does not grow during process execution. So if one batch file is called from other batch files and none of the batch files deletes the temporarily used environment variables, it could happen that there is suddenly no more free memory for a new variable or a larger string for an existing variable during the execution of the batch hierarchy. The logon batch file in large companies is often a collection of batch files which needs more and more memory from environment buffer if each batch file itself called directly or indirectly via other batch file(s) does not remove the temporarily used environment variables.
So it is simply more safe to delete the environment variables used temporary before exiting a batch file although for most batch files it is not necessary and just increases the total batch processing time by a few microseconds.
Using a line like:
set "Folder=%~f1"
is not enough for this batch file. It removes the double quotes from passed directory path and additionally changes a relative directory path to an absolute directory path if the directory can be found.
But it does not remove the backslash from end of a directory path if this path is already an absolute path.
For this batch file it is not really necessary that the directory path is an absolute path as the commands DIR and COPY work both also with relative paths.
The expansion of a relative directory path to an absolute directory path would just require a few microseconds more on batch execution without any benefit, except the batch files is extended by inserting a line with ECHO printing name of copied file with complete path within the FOR loop for visual inspection.

How would I run a batch program from another batch program within its own environment?

I need to run a batch file located in another folder that must be called from another batch file.
Whenever I do call this batch file from the first, let's call them Batch_A and Batch_B, respectively, the second tries to run from the directory of the first batch file.
Batch_A needs to call or start Batch_B, however Batch_B needs to run as if I were to manually double-click it myself.
This is what I currently have at the end of my first batch
start "A thing" "%output%\thing.bat" /b
Have you looked into push or pop.
Before calling the second batch file, enter the "push" command:
pushd %dynamicdirectory%
Call batchfileb.bat
popd
If Batch_B is designed/written to be always run from the direcory where it is located
you might also consider to modify Batch_B.bat
setlocal
cd /D %0\..
REM your original content
endlocal
In %0 the path to the batchfile is stored.
The trick is to assume %0 is a directory then to change one level lower
based on that diretory.
With /D also the drive letter is changed correctly.
The cd command doesn't care if %0 is really a directory.
In fact %d doesn't even have to exist (%0\dummy\..\.. would also work).
The setlocal command is to have the working directory beeing restored
when Batch_B.bat has finished.
I noticed that the endlocal command is not really necessary
in this context since it is applied imlicitely when Batch_B finishes.

Setting a variable from an executable

I am running an executable in a batch file with two parameters;
cmd /k ""executable" "param1" "param2""
This returns a string that I want to launch. I can't figure out how to set this return in a variable and subsequently launch it in IE.
Any ideas?
If the returned string contains a single line you may use FOR /F to set the value of an environment variable. For example:
s1.cmd
echo this is a one line string
s2.cmd
#SETLOCAL
#ECHO OFF
for /f "tokens=*" %%a in ('cmd /c s1.cmd') do set MY_VAR=%%a
echo got: %MY_VAR%
ENDLOCAL
Result
C:\> s2.cmd
got: this is a one line string
C:\>
You can use the following syntax to capture the output of your executable into a variable:
FOR /F "tokens=*" %%i in ('%~dp0YOUR_APP.exe') do SET TOOLOUTPUT=%%i
Source
then you can pass the value on to IE like so:
START "YOUR_WINDOW_NAME" /MAX /D"C:\Program Files\Internet Explorer\" iexplore %TOOLOUTPUT%
I take it that the application code that determines the url is too complicated to be reproduced in a batch file directly, or the source to the executable has been lost. If not I personally would prefer to have the logic visible in the batch file itself.
start %1 %2
Edit: Romulo A. Ceccon posted a much better solution which doesn't involve any file system access and dirty tricks. Left this here for reference (it works with command.com as well if you need 9x compatibility), but please prefer Romulo's solution.
Go through an environment variable you set by using an intermediate helper script you dynamically generate from a template. You will need write permissions somewhere, otherwise it cannot be done (the Windows command shell language is very, very limited.)
Let's call your helper script template helper.tpl with the following contents:
set INTERMEDVAR=
Make sure that helper.tpl has only a single line (no trailing CRLF!) and make sure you don't have any spaces after the equals sign there.
Now, in your main script, capture the output from your command into a temporary file (let's call it my_output_file.tmp):
cmd /k ""executable" "param1" "param2"" > my_output_file.tmp
Then copy the contents of the helper template and the output together into your helper script, let's call it my_helper_script.cmd:
copy /b helper.tpl + my_output_file.tmp my_helper_script.cmd
Then evaluate the helper script in the current context:
call my_helper_script.cmd
Now the INTERMEDVAR variable is set to the first line of the output from "executable" (if it outputs more than one line, you're on your own...) You can now invoke IE:
start iexplore.exe "%INTERMEDVAR%"
And don't forget to clean up the created files:
del /q /f my_output_file.tmp my_helper_script.cmd
This will obviously not work when invoked multiple times in parallel - you'll have to parametrize the temporary file and helper script names using the current cmd.exe's PID (for example) so that they won't overwrite each other's output, but the principle is the same.
However, if you can get a real shell, use that. cmd.exe is extremely cumbersome.

Resources