Variable not being assigned first time from input in a batch script - variables

So I am trying to ask the user if they want to overwrite a current virtual machine if one already exists. I have extracted the problem part of this .bat file into a separate file to replicate the problem.
The code from this file is:
#echo off
#setlocal enabledelayedexpansion
SET test=blah
SET existingMachines=blahsdg
:checkOverwrite
if not "%test%"=="%existingMachines%" (
SET /P machineOverwrite="A machine containing this name already exists. Do you want to overwrite it (y/n)? "
if /I "%machineOverwrite%"=="y" (
echo overwrite
) else (
if /I "%machineOverwrite%"=="n" (
echo You will need to choose a new name for your virtual box or overwite the existing one.
) else (
echo WARNING: You did not enter y or n
GOTO :checkOverwrite
)
)
)
pause
The concept is the first if will always come back as true (as these variables are never equal in this case) and from there it would ask if they want to overwrite the machine. If they say "y", you should see "overwrite" and then press any key to continue...
The issue is it's not doing that! Its doesn't seem to be setting the machineOverwrite variable, and so it is going through to the "you did not enter y or n" section. From here it goes back to the start, and goes through again.
The REAL strange thing is that the next time you go through, if you choose "y", it does what its meant to! BUT, if you choose "n", it still does what it should with "y"!
Every time I enter anything, it always get the input from before it went back to :checkOverwrite and not the latest input. WHY??

Your problem is that despite attempting to enable delayed expansion (with setlocal enabledelayedexpansion) you aren't actually using it.
%var% is normal variable expansion, even with delayed expansion turned on.
You need to use !var! for delayed variable expansion. See (for example) the "documentation" here.

Related

How would I call a dynamic variable name?

Okay, so I'm trying to make a program that "understands" user input and does what they tell it to do. People usually just use specific commands such as "open this file" and it only works if the user types EXACTLY that. I'm trying to give my users a little bit of leeway, so that they can type something like what they want to happen, and the computer will get the general idea. With that block of rambling aside, I've run into a problem.
set word%wordNum%=%word%
:fileExtension
set extChk= %letterNum% - 2 REM Includes the period of the extension
call set extension=%%_albaiRec:~%extChk%,4%%
::extChk is checking for a period so the program will recognize a file extension
set file=
That last line is where I get stuck...
I'm trying to use that last recorded word variable.
set var=7
set word7=Wanted text
echo %word%var%%
Sorta like that?
Add setLocal enableDelayedExpansion to the start of your script.
Then replace echo %word%var%% with echo !word%var%!.
For more information - http://ss64.com/nt/delayedexpansion.html

Variable assignment in batch

This question seems to be (very) stupid be I can't deal with it :(
When I tried this batch code:
if "%1" == "-i" (
set is = %2
echo. %is%
shift
)
called with 2 (or more) arguments, it does NOT work. It actually prints a blank. The "shift" command is not done either. When I watch the executed code (without the #echo off at the beginning), I can see that the "set" command is completed.
What's wrong with it?
Example of calling:
c:\script.bat -i test -d bla
You have two issues. By default group of statements in parens will have variable expansion done all at once, that is before your set command. Also the semantics for set is wrong, you don't want spaces around the =.
Add this to the top of your file:
setlocal ENABLEDELAYEDEXPANSION
and remove the spaces around = in set:
set is=%2
Finally used delayed expansion:
echo. !is!
A possible third issue is you may need two SHIFTs, one for -i, one for it's is argument.
Update
Thanks to #dbenham for pointing out that it wasn't a syntax error with set, it's just surprising behavior that deserves a little explanation. If you execute these commands:
set a=one
echo "%a%"
The result is:
"one"
That makes sense, but try:
set b = two
echo "%b%"
And you get:
"%b%"
What? This is what you would expect when environment var b is unset. But we just set it. Or did we:
echo "%b %"
Displays:
" two"
For the Windows set command, unlike any other language or environment I'm aware of, the spaces are significant. The spaces before the = become part of the environment var name, spaces after become part of the value. This uncommon behavior is a common source of errors when writing Windows batch programs.

How do I reopen programs using CMD from a list or variable

I have a section of code;
:check
for /f "tokens=1* delims==" %%A in ('"wmic process get description, commandline /format:list"') do (
if "%%A"=="CommandLine" (
set "cmd=%%B"
) else if "%%A"=="Description" (
set "desc=%%B"
setlocal enableDelayedExpansion
set "desc=!desc:~0,-1!"
set "cmd=!cmd:~0,-1!"
if /i !desc! == %1 (
echo !cmd! >>C:\test.txt
)
endlocal
)
)
goto:eof
Which pretty much works (this is actually a function called from withing a batch file e.g
call:check processname1.exe
call:check processname2.exe
call:check processname3.exe
etc...
What I'd like to do (if possible), is, insead of echoing to a file, I'd like to be able to create 2 variables. something like;
processname1.exe processname3.exe <-- (for each process 'checked' if it IS running, append its name to this variable)
commandlinepath1 commandlinepath2 <-- (for each process 'checked' if it IS running, append its path to this variable)
If this is possible, and I can then call on these variable later in my script, I'd like to be able to tskill the running processes (easy enough if the variables above can be made), then later on, RE-OPEN these processes (using and command line parameter that were in the original path. This is where I'm lost.
My code above (writing to a file). will give results like;
"C:\somefolder\someexe.exe" -some_parameter
"C:\some therfolder\someotherexe.exe"
"C:\another older\anotherexe.exe" param1 param2
But What I need to do, is take each line of this file (or variable if possible), and run them (if I copy each line into the RUN command of windows, it works, but doing it through CMD it doesn't).
I've tried using a for loop to open the files, and it does, except the script waits for the process to finish beford continuing (and these process won't end, since they are applications). If I try to use START .. then it loads a new CMD window??
What I need to do (in case there is a better option) is
for a pre-determined set of processes, check to see if they are running
kill the ones that are (if they are not, fine ignore it)
delete some files (I can do this, the reason for killin the processes is they hold the files open, preventing deletion)
Re-open all the programs that were originally running
Thanks..
Not a direct answer, but since you already use wmic, maybe using its built in capabilities (query, start & stop) would make your goal easier to achieve?
I come up with the following:
#echo off
setlocal
for %%C in (notepad.exe) do (
for /f "skip=1 tokens=2 delims==" %%F in ('wmic process where description^="%%C" get commandline /format:list') do (
REM required to normalize unicode output from WMIC
set commandline=%%F
REM '\=\\' required as wmic treats \ as escape char in query
call wmic process where commandline='%%commandline:\=\\%%' terminate
REM do your work here
call wmic process call create '%%commandline%%'
)
)
What it does:
First for supplies process names. In my example, it simply is notepad.exe, but you could call with a list: for (process1 process2 process3), or replace it with for /f to supply values from file. If you want to use quoted names, you would have to remove quote from next line (description^="%%C").
Second for does real work: it gets a list of all processes matching description and sequentially stops and starts each of them.
To try it, simply put it in a batfile.bat, open notepad(s) and execute.
Note: if you open notepad with a file, either specify an absolute path, or do it via explorer (double click). The issue here is of current directory - which you could also stumble upon if any of your processes does reference relative paths (unlikely, but not impossible)
Last but not least - doing that in powershell would be the easiest, shortest and most reliable.

Checking for file existence

I have the following bat but it dosnt seem to work I want to check for a file name stoted in tom.txt, if it exists i want to do nothing, if however it dont exist i want to run the runme.bat
Echo Setting variable to file name
set FAT=<C:\tom.txt
ECHO Checking for file, if exists do nothing if not run bat...
if exists %FAT% (
end
)else(
C:\runme.bat
)
There are some minor mistakes, which, however, are the cause of your major difficulties.
You can read a line from a file with the SET /P command, not simply with SET:
SET /P FAT=<C:\tom.txt
The keyword in the file existence check command is EXIST, not EXISTS
IF EXIST …
Also, if you only need to react to the file's non-existence, you can simply add NOT:
IF NOT EXIST …
So, the entire command might be like this:
IF NOT EXIST %FAT% C:\runme.bat
I believe the correct syntax is
if exist %FAT% goto NORUN
C:\runme.bat
:NORUN
Notice "exist" vs "exists" in your code. A couple other things to note:
File NUL exists in any directory on any drive, therefore checking
for C:\NUL will always return true.
Checking for file existence
does not always work correctly on network devices.
See http://support.microsoft.com/kb/65994 for a bit more information.
I think the simplest solution would be to do it all on one line like this:
IF NOT EXIST C:\tom.txt C:\runme.bat
There is no need for the variable unless you intend on using it again, it just means another line of code. As Aleks G and Andriy M said, you need to make sure the commands and parameters are spelt correctly.

Can I control a variable from one batch file with another?

I'm using two batch files, and I need to control variables in one of them, from the other. Is this possible?
You cannot directly influence one process' environment from another process. You know, we've kinda outgrown ye olde days of real mode by now :-)
This all depends a bit on what you're trying to achieve here. If you're calling one batch file from the other, as in
call second.cmd
then the called one »inherits« the environment of the parent batch. So any variable you defined earlier will continue to exist in the child batch. You cannot propagate changes up to the parent, though and you cannot change a variable in the child batch after it has been started, too. It might still be a viable option if all you need is to perform some one-time initialization before starting the child batch.
What you could do is to agree on a file used by both batch files that they will use as a means of communicating with each other, likely located in the temporary directory. Each batch file would need to regularly check for the file to be present and if so, read it and update its variables accordingly. For that to succeed you need points in the batch files where they can look for that file. The simplest would be two files that simply do a bit communication with each other:
The code for that is here:
callchat.cmd:
#echo off
set SENDFILE=%TEMP%\1.out
set RCVFILE=%TEMP%\1.in
start call chat.cmd
ping -w 5000 -n 1 123.45.67.89 >nul 2>&1
set RCVFILE=%TEMP%\1.out
set SENDFILE=%TEMP%\1.in
start call chat.cmd
chat.cmd:
#echo off
setlocal enabledelayedexpansion
rem Prevent direct use
if not defined SENDFILE goto :eof
if not defined RCVFILE goto :eof
set MESSAGE0=I don't know what to say ...
set MESSAGE1=Foo
set MESSAGE2=Bar
set MESSAGE3=Hey there!
set MESSAGE4=Meow.
:loop
rem wait a bit
ping -n 1 -w 1000 123.45.67.89 >nul 2>&1
rem look whether we need to show something
if exist %RCVFILE% (
for /f "delims=" %%l in (%RCVFILE%) do echo Received message at %TIME% - %%l
del "%RCVFILE%"
)
rem randomly send out messages. Roughly ever three times we try this
set /a msg=%random% %% 5
set msg=!MESSAGE%msg%!
if %RANDOM% LSS 10000 (
>>%SENDFILE% echo(%msg%
echo(Sent message "%msg%"
)
goto loop
The batch file is started twice, with different input/output files – in fact, the reversed role of the files from the first invocation. Then it's little more than an endless loop that looks into its input file and read what's in there and writing stuff to its output file (which is the input file for the other batch).
I had to introduce a delay in starting both of them to avoid the PRNGs for both being exactly the same. It also reduced the cases where access to the file failed (this could probably be alleviated by renaming it before reading from it – or, if writing longer content, renaming it to its final name only after being done writing). It's just a simple demo application to show you that it might be possible that way.
To set environment variables you wouldn't print out what's in the file but call it as a batch file, for example:
if exist %RCVFILE% call %RCVFILE%
It would need the proper extension for that, though. You can also read it line by line and have each line contain a VARIABLE=VALUE pair:
if exist %RCVFILE% call for /f "tokens=1* delims==" %%a in (%RCVFILE%) do set %%a=%%b
The techniques mentioned above for improving reliability when accessing the same file from two different programs still apply.
As mentioned, this is only a rough idea how you could operate, iff I understood your question correctly.