whats the difference between: %%a and %variable% variables? - variables

for /f "tokens=*" %%a in ('find /v ":" "%appdata%\gamelauncher\options.txt" ^| find "menu=a"') do ( set usemenu=a )
for /f "tokens=*" %%a in ('find /v ":" "%appdata%\gamelauncher\options.txt" ^| find "menu=b"') do ( set usemenu=b )
for /f "tokens=*" %%a in ('find /v ":" "%appdata%\gamelauncher\options.txt" ^| find "menu=c"') do ( set usemenu=c )
Right, in this code (which may not work, that what i'm trying to find out) we have this "%%a" in that 'for' command.
First, whats the difference between %variable% and %%a?
Second, can someone explain the 'for' command to me? I have Google'd it way too much and all the explanations seem way to complicated...
What I am trying to do is pull a variable from options.txt, so i can change the menu style of my game launcher. there are 3 styles (a, b and c), so if the options.txt reads "menu=a" how can i get it to set a variable like %usemenu% to the value of a?
Thanks for any help in advance!

%variable% are environment variables. They are set with set and can be accessed with %foo% or !foo! (with delayed expansion if enabled). %%a are special variables created by the for command to represent the current loop item or a token of a current line.
for is probably about the most complicated and powerful part of batch files. If you need loop, then in most cases for has you covered. help for has a summary.
You can
iterate over files: for %x in (*.txt) do ...
repeat something n times: for /l %x in (1, 1, 15) do... (the arguments are start, step and end)
iterate over a set of values: for %x in (a, b, c) do ...
iterate over the lines of a file: for /f %x in (foo.txt) do ...
tokenize lines of a file: for /f "tokens=2,3* delims=," %x in (foo.txt) do ...
iterate over the output of a command: for /f %x in ('somecommand.exe') do ...
That's just a short overview. It gets more complex but please read the help for that.
Variables of the form %%a (or %a if for is used outside of batch files) are very similar to arguments to batch files and subroutines (%1, %2, ...). Some kinds of expansions can be applied to them, for example to get just the file name and extension if the variable represents a file name with path you can use %%~nxa. A complete overview of those is given in help for.
On the other hand, environment variables have other kinds of special things. You can perform replacements in them via %foo:a=b% would result in %foo% except that every a is replaced by a b. Also you can take substrings: %foo:~4,2%. Descriptions of those things can be found in help set.
As to why %variables% and %%a are different things that's a bit hard to answer and probably just a historical oddity. As outlined above there is a third kind of variable, %1, etc. which are very similar to those used in for and have existed for longer, I guess. Since environment variables are a bit unwieldy to use in for due to blocks and thus heavy reliance on delayed expansion the decision probably was made to use the same mechanisms as for arguments instead of environment variables.
Also environment variables could be more expensive, given that the process has a special “environment” block of memory where they are stored in variable=value␀ pairs, so updating environment variables involves potentially copying around a bit of memory while the other kind of variables could be more lightweight. This is speculation, though.
As for your problem, you don't really need for here:
find /v ":" "%appdata%\gamelauncher\options.txt" | find "menu=a" && set usemenu=a
This will only run the set if the preceding command was successful, i.e. menu=a was found. This should be considerably easier than for. From what I read you're trying to look whether menu=a exists in a line that does not contain a colon and in that case usemenu should be set to a, right? (And likewise for b and c. You could try coaxing for into doing that by looping over the lines of the file or output and tokenizing appropriately to figure out the value of menu but depending on the format of the lines this can be tricky. If what you have there works in theory then you should simply stick to that. You can however use a loop around it to avoid having to repeat the same line three times for a, b and c:
for %%X in (a b c) do (
find /v ":" "%appdata%\gamelauncher\options.txt" | find "menu=%%X" && set usemenu=%%X
)
If the file you are parsing is simple, however, with just name=value pairs in each line where : foo would be a comment, then you could use for as well:
for /f "tokens=1,* eol=: delims==" %%A in (%appdata%\gamelauncher\options.txt) do (
if "%%A"=="menu" set usemenu=%%B
)
But that depends a little on the exact format of the file. Above snippet would now read the file line by line and for each line would discard everything after a colon (the eol=: option), use the equals sign as a token delimiter and capture two tokens: The part before the first = and everything after it. The tokens are named starting with %%A so the second one is implicitly %%B (again, this is explained in help for). Now, for each line we examine the first token and look whether it's menu and if so, assign its value to the usemenu variable. If you have a lot of possible options to support this is certainly easier to maintain :-).

Related

Using a Batch file can you remove part of a file name from files in a folder and all sub folders?

Hi I am new at using batch files and I am struggling to find a way of removing part of a file name for multiple files in a folder and all sub-folder.
the files are all named like r1_c02_200111_145423_am.csv and I need to remove the _am from the files.
I have tried the following
FOR /R "C:\Users\bob\Documents\data\" %%G IN (*_am.csv) DO REN "%%G" *.csv
but this does not change anything.
can anybody point me in the right direction please?
With the help of such a script, you can perform the required operation.
#ECHO off
FOR %%i IN (*_am.csv) DO CALL :do_rename %%i
GOTO :eof
:do_rename
SET "_file=%1"
REN "%_file%" "%_file:~0,-7%.csv"
:eof
Now more.
The search in the question was correct. To perform a rename while passing through the FOR ... DO, you can use both the SETLOCAL EnableDelayedExpansion or a procedure call. I think it's easier to use a procedure call. you don’t have to puzzle over understanding (and misunderstanding) how the SETLOCAL EnableDelayedExpansion works.
For each iteration of the loop, the Wick procedure is called with the first argument of the file name passed to it:
CALL :do_rename %%i
In the procedure itself, the argument is converted to a variable. You can already use substring operations on the variable itself. More information about substring operations can be found here.
:do_rename
SET "_file=%1"
REN "%_file%" "%_file:~0,-7%.csv"

Value search inside multiple files with results save

I have a very simple task in theory which in practice is quite tedious and time consuming.
I have in excel a list of values (table names) which i need to search individually in a folder filled with files (~ 4800 files) and even if i find or don't find results i need to record either that the table name was not found or all the file names in which the search string was contained.
We are basically trying to locate all custom development reports that make use of custom tables in our database schema. The report queries are contained outside of the DB in a *.txt file.
I'm pretty sure there probably is a way to do this automatically, but my knowledge of script building is quite limited outside SQL.
Edit:
Sorry for the lack of detail - was trying to get a new perspective. I've since used some of the information to run a different type of search for my issue and found a solution which i posted below.
First, get all files that contain your string:
findstr /mic:"table name" > contains_table.txt
Then list all files and filter the previously found names:
dir /b /a-d * | findstr /vg:contains_table.txt > dont_contain_table.txt
Sorry guys. I was trying to get a new perspective on my issue so i tried not to give too many details - but i guess it backfired on me.
But from the answer and comments I realized i was having a brain fart and I managed to run a different search and i found something similar to what i was looking for and for all intents and purposes it does what is needed.
I got the answer from: StackOverflow Thread 25402636
#echo off
setlocal enableextensions disabledelayedexpansion
set "manifest_folder=C:\!Work\!Reports\EMEA\only_reports\*.txt"
set "file_list=C:\!Work\!Reports\EMEA\file_list.txt"
set "outputFile=C:\!Work\!Reports\EMEA\results_file.txt"
(for /f "usebackq delims=" %%a in ("%file_list%") do (
set "found="
for /f "delims=" %%b in ('findstr /l /m /c:"%%a" "%manifest_folder%"') do (
echo %%a is found in %%~nxb
set "found=1"
)
if not defined found (
echo %%a is not found
)
)) > "%outputFile%"

How to .. batch file which executes information in a text file

Sorry not much experience with batch files hence help needed please! ;-)
I'm working in a DOS box on a Windows 7 64 bit system.
I want to run an application as a batch file, but reading the information it needs from a text file which can be updated/amended regularly.
The syntax of the basic application is:
appname "variable" (the variable MUST be enclosed in quotes)
Successive variables can be concatenated to the following single line format:
appname "var1" "var2" "var3" "var4" ... etc
So I've created a batch file containing the above. However, this is unweildy when it comes to updating. Sometimes I omit the delimiting quotes which creates problems in the execution of the batch file.
It seems to me that from an updating/amending point it would be easier to set up a text file, say text.txt which would contain the following information:
"var1"
"var2"
"var3"
"var4"
etc. on successive lines.
This would make it easier for me to update and also to ensure I don't omit the delimiting quotes.
The batch file would get the application to "read" the text file, execute the first variable, then the second etc all the way through to the end. But I'm not sure if this is possible and if so, how to get the batch file to read successive lines in the text file and use those variables.
As I said earlier, I've not much experience with batch files and don't have a clue how to do this! :-(
Help please, thanks
Alan
Like this :
#echo off
set $textFile="test.txt"
for /f "delims=" %%a in ('type %$textFile%') do appname.exe %%a
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
SET "batchline=appname"
FOR /f "delims=" %%a IN (q25193799.txt) DO SET "batchline=!batchline! "%%~a""
ECHO(%batchline%
GOTO :EOF
I used a file named q25193799.txt containing your data for my testing.
The application line is merely echoed for verification. Remove the ECHO( after verification to execute your application.
The data in the file need not have "enclosing quotes".
Thanks to both responses, both different but good in their own way. I use a few different instances and I've decided to use the first response for the longer lists and the second one for shorter lists (that way I can check see if something is going wrong, because I might have missed the second delimiting quote in the text file.
Superb, thanks to you both.

batch files, variable expansion and substrings in loops

I can't wrap my head around the variable substitution mechanism of DOS batch files, especially in for loops.
Simplified batch problem: imagine the following directory
01foo.txt
02foo.dir (this is a directory)
bar01 (this is a directory)
bar02 (this is a directory)
i want to move all files/directories in this directory that do NOT start with 'bar' to a subdirectory that is bar+_the_first_2_characters_of_the_filename_or_directory_name.
In this case, 01foo.txt would be moved into bar01 and 02foo.dir would be moved into bar02.
The following script is my first attempt:
setlocal EnableDelayedExpansion
for %%A in (*) do (
set _x=%%A
if (!_x:~0,2! NEQ "bar") move %%A bar!_x:~0,2!
)
endlocal
apart from the fact that this seems to loop only for files, it simply doesn't work at all :-).
I get an error in the if statement saying "3! was unexpected at this time"
Any idea on how to improve the situation/script?
Thanks
It's only a problem of the syntax...
The IF statement does not expect not accept surrounding brackets.
The the comma in !_x:~0,2! breaks the IF-statment, you could quote both parts or move it into an own set prefix=!_x:~0,2!" line.
If you quote "bar" you also need to quote "!prefix!".
That's all
setlocal EnableDelayedExpansion
for %%A in (*) do (
set "_x=%%A"
set "prefix=!_x:~0,2!"
if "!prefix!" NEQ "bar" move %%A bar!prefix!
)
endlocal

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.