How to set variable to output of for loop in batch - batch-file

I am reading a text file %tmp%\tmp.txt. I am trying to find a line with a given string (in this example "RemotePath") and it sets the variable netdrive to that line if it is found. Here is my code:
for /f "tokens=*" %%a in (%tmp%\tmp.txt) do echo.%%a|findstr /C:"RemotePath " >nul 2>&1 && set netdrive=%%a
It never sets netdrive to anything, however when i change the set netdrive=%%a to echo %%a >> netdrive.txt the text I want does come up in the text file. Any ideas?

for /f "tokens=*" %%a in ('findstr /C:"RemotePath " "%tmp%\tmp.txt"') do set "netdrive=%%a"
The "problem" with the original code is that each side of a pipe is executed in a separate process, so, in your case, the set command is running in another cmd instance and the variable is set in this separate instance, not in the instance that runs the batch file.

It looks like you are repeatedly setting netdrive to each token from the input file, not the output of findstr. The final assignment probably gives it an empty value from the end of the file. To assign it from the output of findstr, see here: How do I get the result of a command in a variable in windows?

Related

Batch script Read multi line Argument in Variable

I wanted to take list of files to delete from user as a argument. One line per argument.
How can store the list of files separated by new line in a variable.
I am using below command.
Set DeletionFiles=${p:DeleteFiles}"
for %%i in (%DeletionFiles%) do (
echo %%i
)
Then i wanted to iterated them on a loop.
${p:DeleteFiles} will get replaced by it's value from external app, which will contain list of files separated by new line.I can not change it.
#ECHO OFF
SETLOCAL
SET "deletionfiles="
:dloop
SET "deleteme="
SET /p "deleteme=Which file to delete ? "
IF DEFINED deleteme SET "deleteme=%deleteme:"=%"
IF DEFINED deleteme SET "deletionfiles=%deletionfiles%,"%deleteme%""&goto dloop
ECHO delete %deletionfiles:~1%
GOTO :EOF
There is no need to use a newline. Your for command (or a del command) will operate perfectly happily on a comma-(or space-)separated list.
Note that there are certain characters that batch uses for special purposes and batch string-processing may not process them in the expected manner. These characters include % ^ and &.
${p:DeleteFiles} will get replaced by it's value from external app,
which will contain list of files separated by new line.I can not
change it.
After the replacement the batch file looks like:
Set DeletionFiles=file1.jpg
file2.jpg
file3.jpg
"
This isn't a valid batch file anymore.
Furthermore it's a bad idea to modify the batch file itself, as this works only once.
You could place the ${p:DeleteFiles} into another file, like input.txt.
Your batch would look like
echo ${p:DeleteFiles} > input.txt
<external program for replacing the DeleteFiles> input.txt
for /F "tokens=*" %%A in (input.txt) do (
echo File: %%A
)
If I understand you correctly, your external program will generate a list of files. You then want to store this multi-line list to a variable. What do you want to do with the variable once you have it? I assume you want to delete the files, but your question isn't clear on that point, so I'll try to over-answer to cover it.
for /f "delims=" %%a in ('{command that generates your list}') do (
echo Doing stuff to %%a...
echo %%a>>listOfFilesToDelete.txt
set var=%%a
if "%var:~0,7%"="DoNotDelete" copy "%%a" \someArchiveFolder\
del "%%a"
)
This will read each line in your generated list as variable %%a. It will then do whatever command(s) you specify. This way, you can run a command on each of the files in the list. In the above code it's
Printing each line to the console embedded in some text
Outputting it to a file
Checking the first 7 characters of the line against a specified string and then copying it to a folder if it matches
And then deleting it
If you still need to reference each line from your generated list, you can even setup an array-like structure. (See Create list or arrays in Windows Batch)
setlocal EnableDelayedExpansion
:: Capture lines in an 'array'
set /a i=0
for /f "delims=" %%a in ('dir /b') do (
set /a i+=1
set var!i!=%%a
)
:: Loop through the 'array'
for /L %%a in (1,1,%i%) do (
echo Do more stuff with !var%%a!
)
Just like above, this will read each line in your generated list as variable %%a. It will then set a variable var!i! equal to the value of the current line. You can then reference each line as var1, var2, and so on, or, as the second section shows, you can loop through them all using for /L. You'll need to get a grasp on working with delayed expansion variables, though.

How can I insert a command result in the same "echo" line?

I would like to create a logfile with this information :
echo %time%;%date%;%computername%;"findstr /I "%%a" %MYFILES%\Dir_ALL.txt" >> %computername%_File.csv
How can I write something that will not write "findstr /I ..." but the output of this command ?
I would like to have everything in the same line on my output file.
Thank you
write a line without linefeed with set /p, followed by the second line:
<nul set /p .=%time%;%date%;%computername%;>>%computername%_File.csv
findstr /I "%%a" %MYFILES%\Dir_ALL.txt>>%computername%_File.csv
Note: Because of the %%a I guess, you are using this codefragment inside a forstatement. I suggest using !time! instead of %time% to get the actual time (using delayed expansion of course)
You have to set a variable with the command result. This is answered in Windows Batch help in setting a variable from command output, using a dummy for-loop whose loop variable is set from the content of a file.
That would be something like this:
findstr /I "%%a" %MYFILES%\Dir_ALL.txt >%temp%\temp.txt
for /f %%i in (%temp%\temp.txt) do echo %time%;%date%;%computername%;%%i >> %computername%_File.csv
del %temp%\temp.txt
There is a limitation on this: the variable cannot contain multiple lines. However, rephrasing your script fragment as a loop would probably solve that issue as well.
The MSDN article on set shows some additional features which you can use to control how the data from the file is parsed. Normally it parses the result into tokens separated by spaces. But you can override that using the delims keyword.

put contents of a text file in commandline

in a batch I have
echo VirtualDub.video.AddComment^(0x0000000C,"","%tc%"^)^;>>v:\automazioneclip\virtualdubmod\temp\%%~na.vcf
but now in place of %tc% I would like insert the contents of a text file, all content of a text file
How I have to modify it? thanks
Use SET /P to print out the first portion of the line without a newline. Then use TYPE to print out the contents. Then finish up with a normal ECHO.
<nul (
set /p ^"=VirtualDub.video.AddComment^(0x0000000C,"",""
type file.txt
(echo ^"^);)
) >>"v:\automazioneclip\virtualdubmod\temp\%%~na.vcf"
Note that the closing quote after the file contents will appear on the next line if the file ends with a newline. Obviously the value will be spread across multiple lines if the file contains multiple lines. Multiple lines may or may not be a problem depending on the language of the code you are writing.
Related question: How do you loop through each line in a text file using a windows batch file?
So possibly something like:
for /F "tokens=*" %%A in (myfile.txt) do [process] %%A
where "process" is your line above.
for /F "tokens=*" %%A in (myfile.txt) do echo VirtualDub.video.AddComment^(0x0000000C,"","%%A"^)^;>>v:\automazioneclip\virtualdubmod\temp\%%~na.vcf

Executing command, getting output and using it

I'm trying to do a Windows batch file that excecute a command and pass to it a flow param that is received as a parameter of the batch file, then I need to get the output of this execution and do something with it before sending it to the batch output (cuting the output to get the first character only).
This is what I have so far:
for /f %%i in ('"C:\Program Files (x86)\JAM Software\SpamAssassin for Windows\spamc.exe" < %1') do set RES2 = %%i
ECHO %RES2~0,1%
But it is not working as I expect. Thanks for the help.
for /f %%i in (
'"C:\Program Files (x86)\JAM Software\SpamAssassin for Windows\spamc.exe" ^< %1'
) do set RES2=%%i
ECHO %RES2:~0,1%
There are three changes in your code. The colon in the echo line (needed, it is part of the sintax), the escaped redirection and the spaces removed around the equal sign in the set line. With spaces, the variable name contains a space and the value inside the variable also have a space in it (and it will be the first, so the later echo command will fail)

Windows Batch help in setting a variable from command output [duplicate]

This question already has answers here:
Set output of a command as a variable (with pipes) [duplicate]
(6 answers)
Closed 7 years ago.
I need to run a simple find command and redirect the output to a variable in a Windows Batch File.
I have tried this:
set file=ls|find ".txt"
echo %file%
But it does not work.
If I run this command it works without problems:
set file=test.txt
echo %file%
So obviously my command output is not being set to my variable. Can anyone help? Thanks
I just find out how to use commands with pipes in it, here's my command (that extracts the head revision of an svn repo) :
SET SVN_INFO_CMD=svn info http://mySvnRepo/MyProjects
FOR /f "tokens=1 delims=" %%i IN ('%SVN_INFO_CMD% ^| find "Revision"') DO echo %%i
First of all, what you seem to expect from your question isn't even possible in UNIX shells. How should the shell know that ls|find foo is a command and test.txt is not? What to execute here? That's why UNIX shells have the backtick for such things. Anyway, I digress.
You can't set environment variables to multi-line strings from the shell. So we now have a problem because the output of ls wouldn't quite fit.
What you really want here, though, is a list of all text files, right? Depending on what you need it's very easy to do. The main part in all of these examples is the for loop, iterating over a set of files.
If you just need to do an action for every text file:
for %%i in (*.txt) do echo Doing something with "%%i"
This even works for file names with spaces and it won't erroneously catch files that just have a .txt in the middle of their name, such as foo.txt.bar. Just to point out that your approach isn't as pretty as you'd like it to be.
Anyway, if you want a list of files you can use a little trick to create arrays, or something like that:
setlocal enabledelayedexpansion
set N=0
for %%i in (*.txt) do (
set Files[!N!]=%%i
set /a N+=1
)
After this you will have a number of environment variables, named Files[0], Files[1], etc. each one containing a single file name. You can loop over that with
for /l %%x in (1,1,%N%) do echo.!Files[%%x]!
(Note that we output a superfluous new line here, we could remove that but takes one more line of code :-))
Then you can build a really long line of file names, if you wish. You might recognize the pattern:
setlocal enabledelayedexpansion
set Files=
for %%i in (*.txt) do set Files=!Files! "%%i"
Now we have a really long line with file names. Use it for whatever you wish. This is sometimes handy for passing a bunch of files to another program.
Keep in mind though, that the maximum line length for batch files is around 8190 characters. So that puts a limit on the number of things you can have in a single line. And yes, enumerating a whole bunch of files in a single line might overflow here.
Back to the original point, that batch files have no way of capturing a command output. Others have noted it before. You can use for /f for this purpose:
for /f %%i in ('dir /b') do ...
This will iterate over the lines returned by the command, tokenizing them along the way. Not quite as handy maybe as backticks but close enough and sufficient for most puposes.
By default the tokens are broken up at whitespace, so if you got a file name "Foo bar" then suddenly you would have only "Foo" in %%i and "bar" in %%j. It can be confusing and such things are the main reason why you don't ever want to use for /f just to get a file listing.
You can also use backticks instead of apostrophes if that clashes with some program arguments:
for /f "usebackq" %%i in (`echo I can write 'apostrophes'`) do ...
Note that this also tokenizes. There are some more options you can give. They are detailed in the help for command.
set command has /p option that tells it to read a value from standard input. Unfortunately, it does not support piping into it, but it supports reading a value from a first line of existing file.
So, to set your variable to the name of a first *.txt file, you could do the following:
dir /b *.txt > filename.tmp
set /p file=< filename.tmp
del /q filename.tmp
It is important not to add a space before or even after =.
P. S. No fors, no tokens.
Here's a batch file which will return the last item output by find:
#echo off
ls | find ".txt" > %temp%\temp.txt
for /f %%i in (%temp%\temp.txt) do set file=%%i
del %temp%\temp.txt
echo %file%
for has a syntax for parsing command output, for /f "usebackq", but it cannot handle pipes in the command, so I've redirected output to a temporary location.
I strongly recommend, given that you have access to ls, that you consider using a better batch language, such as bash or even an scripting language like python or ruby. Even bash would be a 20x improvement over cmd scripting.
The short answer is: Don't!
A windows shell env var can hold a max of 32 Kb and it isn't safe to save output from programs in them.
That's why you can't. In batch script you must adopt another programming style. If you need all of the output
from the program then save it to file. If you only need to check for certain properties then pipe the output into
a program that does the checking and use the errorlevel mechanism:
#echo off
type somefile.txt | find "somestring" >nul
if %errorlevel% EQU 1 echo Sorry, not found!
REM Alternatively:
if errorlevel 1 echo Sorry, not found!
However, it's more elegant to use the logical operators Perl style:
#echo off
(type somefile.txt | find "somestring" >nul) || echo Sorry, not found!
It's not available in DOS, but in the Windows console, there is the for command. Just type 'help for' at a command prompt to see all of the options. To set a single variable you can use this:
for /f %%i in ('find .txt') do set file=%%i
Note this will only work for the first line returned from 'find .txt' because windows only expands variable once by default. You'll have to enable delayed expansion as shown here.
what you are essentially doing is listing out .txt files. With that, you can use a for loop to over dir cmd
eg
for /f "tokens=*" %%i in ('dir /b *.txt') do set file=%%i
or if you prefer using your ls, there's no need to pipe to find.
for /f "tokens=*" %%i in ('ls *.txt') do set file=%%i
Example of setting a variable from command output:
FOR /F "usebackq" %%Z IN ( `C:\cygwin\bin\cygpath "C:\scripts\sample.sh"` ) DO SET BASH_SCRIPT=%%Z
c:\cygwin\bin\bash -c '. ~/.bashrc ; %BASH_SCRIPT%'
Also, note that if you want to test out the FOR command in a DOS shell, then you need only use %Z instead of %%Z, otherwise it will complain with the following error:
%%Z was unexpected at this time.

Resources