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

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.

Related

How to use command prompt to list file names in directory but exclude 1st 3 characters

I'm trying to write some vba code that sends a line of code to the command prompt and executes it. I have that part down, but I need help getting the actual code to work.
I want to list all of the files in a specific folder that are the .doc file extension, but I want to exclude the first three characters of the filename that gets printed to my output text file. (Note: I'm using vba because this is one of several different commands I'd like to get into a single vba macro, and I cannot use batch files b/c they are blocked on my system so I'd like to work directly with the command prompt)
The following code works and gives me the file names without the file extension (ie. ABC201704.doc will return as ABC201704)
*%comspec% /c for %i in (C:\Test\ABC*.doc) do #echo %~ni >> C:\Test\Output.txt*
However, I don't know how to modify this so that it doesn't include the first 3 characters (ie. I'd like it to return 201704 instead of ABC201704).
Any help would be greatly appreciated! I tried using the following link, but I couldn't figure out how to get that to work for my situation.
Not tested:
#echo off
setlocal enableDelayedExpansion
for %%a in ("C:\Test\ABC*.doc") do (
set docname=%%~nxa
echo !docname:~3!
)
In command prompt:
cmd /v:on /c "for %a in ("C:\Test\ABC*.doc") do set docname=%~nxa & echo !docname:~3!"

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

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.

Best way to process set variables in batch files

This question is probably unnecessary as I probably have been searching for the wrong criteria. It's pretty easy I think but I just can't find the answer. I use batch files to automate installs with my work, setting folder permissions, copying files here, and there, silently running programs, removing old ones etc. The batch files contain lots of repetition and I want to tidy them up greatly for easier management. I have decided to set the repeated commands and copy folder locations as variables then use them instead. This is all fine but I'm adding the variables as a LIST which looks rubbish to me, I'm sure I can load them together in a sentence rather than a new line for each. Here's what I mean...
set dir1=md c:\newfolder
set killtask=taskkill /im someprog.exe /f >nul 2>&1
set config=echo F| XCOPY %~dp0configfile3.cfg /y C:\newfolder
And on and on...
So at the minute my batch now looks like this
JOB1
%dir1%
%killtask%
%config%
What I want to be able to do is have it like this (to reduce length if batch file etc.)
JOB1
%dir1% %killtask% %config%
(summary of my comments to the question):
Check your variables! Some chars like pipe (|) or redirection (>) change how the line is interpreted. Use this syntax to correct it:
set "killtask=taskkill /im someprog.exe /f >nul 2>&1"
(note the quotes and their position)
you can call several commands with &:
echo hello&echo world.
With your complex variables this would probably fail/work in an unexpected way. Try
(%dir1%)&(%killtask%)&(%config%)
so that the redirections and pipes stay at their intended commands.

Creating a bat file which executes SQL scripts

I have a folder into which a number of MSQL scripts get dropped into after each weekly sprint. For example, 10 scripts were placed into the folder today. I had to then open each script individually and run it against the applicable database. The database that it needs to be run against is in the name of the file.
e.g. [2] [CRMdata]UpdateProc.sql
The [2] represents the sequence in which it is run, so script [1] needs to be run before it.
[CRMdata] is the database I have to run it against.
This process is very tiresome, especially if there are 50 scripts to run sequentially.
I was wondering if there was an easier way to do this?
Perhpas a .bat file, which reads the filename, and executes the scripts sequentially based on the script number, as well as executing it against the database specified in the file name.
Any help is much appreciated.
Thanks.
First, when you need to run things, consider using SQL Server Job Agent. This is a good way to schedule simple things.
For a task like this, I would recommend PowerShell in combination with "sqlcmd". This command is actually the answer to your question, since it will run scripts from the command line.
However, go a step further. Schedule a job that runs once per week (or whenever you want it run). Have it consist of one step, a PowerShell script. This can then loop through all the scripts in the directory, extract the file name from the name, and run the script using sqlcmd. Along the way, also log what you are doing in a table so you can spot errors.
I don't know anything about executing SQL with MSQL. You will have to work out how to run each script against the proper database using whatever command-line utility is provided for MSQL.
I can help you with a batch file that will sort the SQL files in the correct sequence order, and parse out the name of the database.
The job is much easier in batch if the sequence numbers are zero prefixed to be a constant width. I'm assuming it is OK to rename the files, so that is what this solution does.
I also assumed you will never have more than 999 files to process. The code can easily be modified to handle more.
Some changes will have to be made if any file names contain the ! character because delayed expansion will corrupt the expansion of the FOR variables. But that is an unlikely problem.
#echo off
setlocal enableDelayedExpansion
:: Change the definition to point to the folder that contains the scripts
set "folder=sqlCodeFolder"
:: The mask will only match the pattern that you indicated in your question
set "mask=[*] [*]*.sql"
:: Rename the .sql files so that the sequence numbers are zero prefixed
:: to width of 3. This enables the default alpha sort of the directory to be
:: in the proper sequence
for /f "tokens=1* delims=[]" %%A in ('dir /b "%folder%\%mask%"') do (
set seq=00%%A
ren "%folder%\[%%A]%%B" "[!seq:~-3!]%%B"
)
::Process the renamed files in order
for %%F in ("%folder%\%mask%") do (
for /f "tokens=2 delims=[] " %%D in ("%%~nF") do (
rem %%F contains the full path to the sql file
rem %%D contains the name of the database, without enclosing []
rem Replace the echo line below with the proper command to run your script
echo run %%F against database [%%D]
)
)

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.