Related
I am running a script that goes through network folders and saves the found files, however it is taking too long to run. I have tried isolating the findstr to a single folder and it runs at an okay time, so I assume it has something to do with the FOR loop.
#echo off
setlocal
set SERVERS=server1 server2 server3 server4
cls
echo Type below the query parameters:
set /p year=Year (4 digits):
set /p month=Month (2 digits):
set /p day=Day (2 digits):
set /p query=Query string:
cls
echo Results:
del /F /Q "C:\Users\%USERNAME%\Desktop\found_files\*" 2>nul
if not exist "C:\Users\%USERNAME%\Desktop\found_files" mkdir "C:\Users\%USERNAME%\Desktop\found_files"
for /f "tokens=*" %%a in ('for %%i in ^(%SERVERS%^) do #findstr /S /I /M /C:"%query%" "\\%%i\folder_structure\*%year%-%month%-%day%*.xml"') do copy /Y "%%a" "C:\Users\%USERNAME%\Desktop\found_files" >nul & echo %%a & set found=1
echo.
if "%found%"=="1" (
echo File^(s^) saved successfully!
) else (
echo No files found!
)
echo.
pause
if "%found%"=="1" explorer C:\Users\%USERNAME%\Desktop\found_files
Your script is already optimized pretty well. I don't think there is much you can do to speed things up.
I suspect your problem is that FINDSTR is running on your local machine, and it must scan the files on all UNC paths (almost surely not local). This means the entire content of every file must be transmitted across your network. If your system is anything like where I work, that could be a nightmare. Our network drive performance is pathetic (more than a factor of 100 slower than local drive)!
Squashman (and SomethingDark) were somewhat concerned about your outer FOR /F executing a nested FOR statement. But I believe that is the most efficient way. When FOR /F iterates command output, it must launch a new process to execute the command. Your current script only needs one sub-process.
The more "traditional" approach would be to move the %SERVERS% iteration outside the inner loop as follows:
for %%i in (%SERVERS%) do for /f "tokens=*" %%a in (
'findstr /S /I /M /C:"%query%" "\\%%i\folder_structure\*%year%-%month%-%day%*.xml"'
) do copy /Y "%%a" "C:\Users\%USERNAME%\Desktop\found_files" >nul & echo %%a & set found=1
But this is actually less efficient because it must launch a new sub-process for each UNC path within %SERVERS%. That being said, I don't think the difference is significant compared to the actual bottle neck of transmitting the file content across the network.
To show the impact of one vs. 100 sub-processes, I ran a quick comparison of the following logically equivalent (but meaningless) commands:
for /f "delims=" %%F in (
'for /l %%N in ^(1 1 100^) do #findstr /m "^" *'
) do echo %%F>nul
:: This took 39 seconds on my machine
for /l %%N in (1 1 100) do for /f %%F in (
'findstr /m "^" *'
) do echo %%F>nul
:: This took 60.9 seconds on my machine
#echo off
set SERVERS=server1,server2,server3,server4
cls
echo Type below the query parameters:
:: Type echo %date% on command prompt if its returns the current date dd/mm/yyyy format, you can load the variables using a substring:
set year=%date:~6,4%
set month=%date:~3,2%
set day=%date:~0,2%
set /p query=Query string:
:: set counter for files founded
set found=0
cls
echo Results:
if not exist "C:\Users\%USERNAME%\Desktop\found_files" (mkdir "C:\Users\%USERNAME%\Desktop\found_files") else (del /F /Q "C:\Users\%USERNAME%\Desktop\found_files\*" 2>nul)
for /f %%i in (%SERVERS%) do ('#find /i /c "%query%" "\\%%i\folder_structure\*%year%-%month%-%day%*.xml"') do (
if "%%i"=="1" (set /a found=%found%+1 && copy /Y "\\%%i\folder_structure\*%year%-%month%-%day%*.xml" "C:\Users\%USERNAME%\Desktop\found_files" >nul && echo File^(s^) saved successfully! & echo.) else (echo No files found!)
)
echo.
pause
if %found% gtr 0 (explorer C:\Users\%USERNAME%\Desktop\found_files)
This question already has answers here:
Assign output of a program to a variable using a MS batch file
(12 answers)
Closed 9 months ago.
Is it possible to set a statement's output of a batch file to a variable, for example:
findstr testing > %VARIABLE%
echo %VARIABLE%
FOR /F "tokens=* USEBACKQ" %%F IN (`command`) DO (
SET var=%%F
)
ECHO %var%
I always use the USEBACKQ so that if you have a string to insert or a long file name, you can use your double quotes without screwing up the command.
Now if your output will contain multiple lines, you can do this
SETLOCAL ENABLEDELAYEDEXPANSION
SET count=1
FOR /F "tokens=* USEBACKQ" %%F IN (`command`) DO (
SET var!count!=%%F
SET /a count=!count!+1
)
ECHO %var1%
ECHO %var2%
ECHO %var3%
ENDLOCAL
In a single line:
FOR /F "tokens=*" %%g IN ('your command') do (SET VAR=%%g)
The command output will be set in %g then in VAR.
More information here: https://ss64.com/nt/for_cmd.html
I found this thread on that there Interweb thing. Boils down to:
#echo off
setlocal enableextensions
for /f "tokens=*" %%a in (
'VER'
) do (
set myvar=%%a
)
echo/%%myvar%%=%myvar%
pause
endlocal
You can also redirect the output of a command to a temporary file, and then put the contents of that temporary file into your variable, likesuchashereby. It doesn't work with multiline input though.
cmd > tmpFile
set /p myvar= < tmpFile
del tmpFile
Credit to the thread on Tom's Hardware.
If you don't want to output to a temp file and then read into a variable, this code stores result of command direct into a variable:
FOR /F %i IN ('findstr testing') DO set VARIABLE=%i
echo %VARIABLE%
If you want to enclose search string in double quotes:
FOR /F %i IN ('findstr "testing"') DO set VARIABLE=%i
If you want to store this code in a batch file, add an extra % symbol:
FOR /F %%i IN ('findstr "testing"') DO set VARIABLE=%%i
A useful example to count the number of files in a directory & store in a variable:
(illustrates piping)
FOR /F %i IN ('dir /b /a-d "%cd%" ^| find /v /c "?"') DO set /a count=%i
Note the use of single quotes instead of double quotes " or grave accent ` in the command brackets. This is cleaner alternative to delims, tokens or usebackq in for loop.
Update 27/8/2021:
Another method is to set errorlevel variable, although many would discourage setting errorlevel on large scripts or when new to cmd flavor of the installed OS variant.
This method works where (return) value to be stored is a 32-bit integer.
eg.
to count the number of files in a directory & store in a variable called errorlevel:
(dir /b /a-d ^| find /v /c "?") | (set /p myVar=& cmd /c exit /b %myVar%)
echo %errorlevel%
81
set /a %errorlevel%+1
82
REM Note: Win CMD arithmetic limit: 2147483647 (32-bit integers)
REM ie. an overflow would continue count at -2147483648
REM and reset again after reaching 2147483647
REM See tests below:
cmd /c exit /b 2147483647
echo %errorlevel%
2147483647
cmd /c exit /b 2147483648
echo %errorlevel%
-2147483648
cmd /c exit /b 2147483649
echo %errorlevel%
-2147483647
Above method can be modified to return encoded strings to be decoded in parent process (within 32 bit limitation).
A 3rd illustration, although of limited use (because the variable is set in child process, not parent) is:
(dir /b /a-d ^| find /v /c "?") | (set /p myVar=& set myVar)
In this case the value of myVar is set to the number of files in the directory
Tested on Win 10 CMD.
To read a file...
set /P Variable=<File.txt
To Write a file
#echo %DataToWrite%>File.txt
note; having spaces before the <> character causes a space to be added at the end of the variable, also
To add to a file,like a logger program,
First make a file with a single enter key in it called e.txt
set /P Data=<log0.log
set /P Ekey=<e.txt
#echo %Data%%Ekey%%NewData%>log0.txt
your log will look like this
Entry1
Entry2
and so on
Anyways a couple useful things
These answers were all so close to the answer that I needed. This is an attempt to expand on them.
In a Batch file
If you're running from within a .bat file and you want a single line that allows you to export a complicated command like jq -r ".Credentials.AccessKeyId" c:\temp\mfa-getCreds.json to a variable named AWS_ACCESS_KEY then you want this:
FOR /F "tokens=* USEBACKQ" %%g IN (`jq -r ".Credentials.AccessKeyId" c:\temp\mfa-getCreds.json`) do (SET "AWS_ACCESS_KEY=%%g")
On the Command Line
If you're at the C:\ prompt you want a single line that allows you to run a complicated command like jq -r ".Credentials.AccessKeyId" c:\temp\mfa-getCreds.json to a variable named AWS_ACCESS_KEY then you want this:
FOR /F "tokens=* USEBACKQ" %g IN (`jq -r ".Credentials.AccessKeyId" c:\temp\mfa-getCreds.json`) do (SET "AWS_ACCESS_KEY=%g")
Explanation
The only difference between the two answers above is that on the command line, you use a single % in your variable. In a batch file, you have to double up on the percentage signs (%%).
Since the command includes colons, quotes, and parentheses, you need to include the USEBACKQ line in the options so that you can use backquotes to specify the command to run and then all kinds of funny characters inside of it.
Some notes and some tricks.
The 'official' way to assign result to a variable is with FOR /F though in the other answers is shown how a temporary file can be used also.
For command processing FOR command has two forms depending if the usebackq option is used. In the all examples below the whole output is used without splitting it.
FOR /f "tokens=* delims=" %%A in ('whoami') do #set "I-Am=%%A"
FOR /f "usebackq tokens=* delims=" %%A in (`whoami`) do #set "I-Am=%%A"
and if used directly in the console:
FOR /f "tokens=* delims=" %A in ('whoami') do set "I-Am=%A"
FOR /f "usebackq tokens=* delims=" %A in (`whoami`) do set "I-Am=%A"
%%A is a temporary variable available only on the FOR command context and is called token.The two forms can be useful in case when you are dealing with arguments containing specific quotes. It is especially useful with REPL interfaces of other languages or WMIC.
Though in both cases the expression can be put in double quotes and it still be processed.
Here's an example with python (it is possible to transition the expression in the brackets on a separate line which is used for easier reading):
#echo off
for /f "tokens=* delims=" %%a in (
'"python -c ""print("""Message from python""")"""'
) do (
echo processed message from python: "%%a"
)
To use an assigned variable in the same FOR block check also the DELAYED EXPANSION
And some tricks
To save yourself from writing all the arguments for the FOR command you can use MACRO for assigning the result to variable:
#echo off
::::: ---- defining the assign macro ---- ::::::::
setlocal DisableDelayedExpansion
(set LF=^
%=EMPTY=%
)
set ^"\n=^^^%LF%%LF%^%LF%%LF%^^"
::set argv=Empty
set assign=for /L %%n in (1 1 2) do ( %\n%
if %%n==2 (%\n%
setlocal enableDelayedExpansion%\n%
for /F "tokens=1,2 delims=," %%A in ("!argv!") do (%\n%
for /f "tokens=* delims=" %%# in ('%%~A') do endlocal^&set "%%~B=%%#" %\n%
) %\n%
) %\n%
) ^& set argv=,
::::: -------- ::::::::
:::EXAMPLE
%assign% "WHOAMI /LOGONID",result
echo %result%
the first argument to the macro is the command and the second the name of the variable we want to use and both are separated by , (comma). Though this is suitable only for straight forward scenarios.
If we want a similar macro for the console we can use DOSKEY
doskey assign=for /f "tokens=1,2 delims=," %a in ("$*") do #for /f "tokens=* delims=" %# in ('"%a"') do #set "%b=%#"
rem -- example --
assign WHOAMI /LOGONID,my-id
echo %my-id%
DOSKEY does accept double quotes as enclosion for arguments so this also is useful for more simple scenarios.
FOR also works well with pipes which can be used for chaining commands (though it is not so good for assigning a variable.
hostname |for /f "tokens=* delims=" %%# in ('more') do #(ping %%#)
Which also can be beautified with macros:
#echo off
:: --- defining chain command macros ---
set "result-as-[arg]:=|for /f "tokens=* delims=" %%# in ('more') do #("
set "[arg]= %%#)"
::: -------------------------- :::
::Example:
hostname %result-as-[arg]:% ping %[arg]%
And for completnes macros for the temp file approach (no doskey definition ,but it also can be easy done.If you have a SSD this wont be so slow):
#echo off
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
set "[[=>"#" 2>&1&set/p "&set "]]==<# & del /q # >nul 2>&1"
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
chcp %[[%code-page%]]%
echo ~~%code-page%~~
whoami %[[%its-me%]]%
echo ##%its-me%##
For /f with another macro:
::::::::::::::::::::::::::::::::::::::::::::::::::
;;set "{{=for /f "tokens=* delims=" %%# in ('" &::
;;set "--=') do #set "" &::
;;set "}}==%%#"" &::
::::::::::::::::::::::::::::::::::::::::::::::::::
:: --examples
::assigning ver output to %win-ver% variable
%{{% ver %--%win-ver%}}%
echo 3: %win-ver%
::assigning hostname output to %my-host% variable
%{{% hostname %--%my-host%}}%
echo 4: %my-host%
cd %windir%\system32\inetsrv
#echo off
for /F "tokens=* USEBACKQ" %%x in (
`appcmd list apppool /text:name`
) do (
echo|set /p= "%%x - " /text:name & appcmd.exe list apppool "%%x" /text:processModel.identityType
)
echo %date% & echo %time%
pause
I most cases, creating a temporary file named after your variable name might be acceptable. (as you are probably using meaningful variables name...)
Here, my variable name is SSH_PAGEANT_AUTH_SOCK
dir /w "\\.\pipe\\"|find "pageant" > %temp%\SSH_PAGEANT_AUTH_SOCK && set /P SSH_PAGEANT_AUTH_SOCK=<%temp%\SSH_PAGEANT_AUTH_SOCK
This question already has answers here:
Assign output of a program to a variable using a MS batch file
(12 answers)
Closed 9 months ago.
Is it possible to set a statement's output of a batch file to a variable, for example:
findstr testing > %VARIABLE%
echo %VARIABLE%
FOR /F "tokens=* USEBACKQ" %%F IN (`command`) DO (
SET var=%%F
)
ECHO %var%
I always use the USEBACKQ so that if you have a string to insert or a long file name, you can use your double quotes without screwing up the command.
Now if your output will contain multiple lines, you can do this
SETLOCAL ENABLEDELAYEDEXPANSION
SET count=1
FOR /F "tokens=* USEBACKQ" %%F IN (`command`) DO (
SET var!count!=%%F
SET /a count=!count!+1
)
ECHO %var1%
ECHO %var2%
ECHO %var3%
ENDLOCAL
In a single line:
FOR /F "tokens=*" %%g IN ('your command') do (SET VAR=%%g)
The command output will be set in %g then in VAR.
More information here: https://ss64.com/nt/for_cmd.html
I found this thread on that there Interweb thing. Boils down to:
#echo off
setlocal enableextensions
for /f "tokens=*" %%a in (
'VER'
) do (
set myvar=%%a
)
echo/%%myvar%%=%myvar%
pause
endlocal
You can also redirect the output of a command to a temporary file, and then put the contents of that temporary file into your variable, likesuchashereby. It doesn't work with multiline input though.
cmd > tmpFile
set /p myvar= < tmpFile
del tmpFile
Credit to the thread on Tom's Hardware.
If you don't want to output to a temp file and then read into a variable, this code stores result of command direct into a variable:
FOR /F %i IN ('findstr testing') DO set VARIABLE=%i
echo %VARIABLE%
If you want to enclose search string in double quotes:
FOR /F %i IN ('findstr "testing"') DO set VARIABLE=%i
If you want to store this code in a batch file, add an extra % symbol:
FOR /F %%i IN ('findstr "testing"') DO set VARIABLE=%%i
A useful example to count the number of files in a directory & store in a variable:
(illustrates piping)
FOR /F %i IN ('dir /b /a-d "%cd%" ^| find /v /c "?"') DO set /a count=%i
Note the use of single quotes instead of double quotes " or grave accent ` in the command brackets. This is cleaner alternative to delims, tokens or usebackq in for loop.
Update 27/8/2021:
Another method is to set errorlevel variable, although many would discourage setting errorlevel on large scripts or when new to cmd flavor of the installed OS variant.
This method works where (return) value to be stored is a 32-bit integer.
eg.
to count the number of files in a directory & store in a variable called errorlevel:
(dir /b /a-d ^| find /v /c "?") | (set /p myVar=& cmd /c exit /b %myVar%)
echo %errorlevel%
81
set /a %errorlevel%+1
82
REM Note: Win CMD arithmetic limit: 2147483647 (32-bit integers)
REM ie. an overflow would continue count at -2147483648
REM and reset again after reaching 2147483647
REM See tests below:
cmd /c exit /b 2147483647
echo %errorlevel%
2147483647
cmd /c exit /b 2147483648
echo %errorlevel%
-2147483648
cmd /c exit /b 2147483649
echo %errorlevel%
-2147483647
Above method can be modified to return encoded strings to be decoded in parent process (within 32 bit limitation).
A 3rd illustration, although of limited use (because the variable is set in child process, not parent) is:
(dir /b /a-d ^| find /v /c "?") | (set /p myVar=& set myVar)
In this case the value of myVar is set to the number of files in the directory
Tested on Win 10 CMD.
To read a file...
set /P Variable=<File.txt
To Write a file
#echo %DataToWrite%>File.txt
note; having spaces before the <> character causes a space to be added at the end of the variable, also
To add to a file,like a logger program,
First make a file with a single enter key in it called e.txt
set /P Data=<log0.log
set /P Ekey=<e.txt
#echo %Data%%Ekey%%NewData%>log0.txt
your log will look like this
Entry1
Entry2
and so on
Anyways a couple useful things
These answers were all so close to the answer that I needed. This is an attempt to expand on them.
In a Batch file
If you're running from within a .bat file and you want a single line that allows you to export a complicated command like jq -r ".Credentials.AccessKeyId" c:\temp\mfa-getCreds.json to a variable named AWS_ACCESS_KEY then you want this:
FOR /F "tokens=* USEBACKQ" %%g IN (`jq -r ".Credentials.AccessKeyId" c:\temp\mfa-getCreds.json`) do (SET "AWS_ACCESS_KEY=%%g")
On the Command Line
If you're at the C:\ prompt you want a single line that allows you to run a complicated command like jq -r ".Credentials.AccessKeyId" c:\temp\mfa-getCreds.json to a variable named AWS_ACCESS_KEY then you want this:
FOR /F "tokens=* USEBACKQ" %g IN (`jq -r ".Credentials.AccessKeyId" c:\temp\mfa-getCreds.json`) do (SET "AWS_ACCESS_KEY=%g")
Explanation
The only difference between the two answers above is that on the command line, you use a single % in your variable. In a batch file, you have to double up on the percentage signs (%%).
Since the command includes colons, quotes, and parentheses, you need to include the USEBACKQ line in the options so that you can use backquotes to specify the command to run and then all kinds of funny characters inside of it.
Some notes and some tricks.
The 'official' way to assign result to a variable is with FOR /F though in the other answers is shown how a temporary file can be used also.
For command processing FOR command has two forms depending if the usebackq option is used. In the all examples below the whole output is used without splitting it.
FOR /f "tokens=* delims=" %%A in ('whoami') do #set "I-Am=%%A"
FOR /f "usebackq tokens=* delims=" %%A in (`whoami`) do #set "I-Am=%%A"
and if used directly in the console:
FOR /f "tokens=* delims=" %A in ('whoami') do set "I-Am=%A"
FOR /f "usebackq tokens=* delims=" %A in (`whoami`) do set "I-Am=%A"
%%A is a temporary variable available only on the FOR command context and is called token.The two forms can be useful in case when you are dealing with arguments containing specific quotes. It is especially useful with REPL interfaces of other languages or WMIC.
Though in both cases the expression can be put in double quotes and it still be processed.
Here's an example with python (it is possible to transition the expression in the brackets on a separate line which is used for easier reading):
#echo off
for /f "tokens=* delims=" %%a in (
'"python -c ""print("""Message from python""")"""'
) do (
echo processed message from python: "%%a"
)
To use an assigned variable in the same FOR block check also the DELAYED EXPANSION
And some tricks
To save yourself from writing all the arguments for the FOR command you can use MACRO for assigning the result to variable:
#echo off
::::: ---- defining the assign macro ---- ::::::::
setlocal DisableDelayedExpansion
(set LF=^
%=EMPTY=%
)
set ^"\n=^^^%LF%%LF%^%LF%%LF%^^"
::set argv=Empty
set assign=for /L %%n in (1 1 2) do ( %\n%
if %%n==2 (%\n%
setlocal enableDelayedExpansion%\n%
for /F "tokens=1,2 delims=," %%A in ("!argv!") do (%\n%
for /f "tokens=* delims=" %%# in ('%%~A') do endlocal^&set "%%~B=%%#" %\n%
) %\n%
) %\n%
) ^& set argv=,
::::: -------- ::::::::
:::EXAMPLE
%assign% "WHOAMI /LOGONID",result
echo %result%
the first argument to the macro is the command and the second the name of the variable we want to use and both are separated by , (comma). Though this is suitable only for straight forward scenarios.
If we want a similar macro for the console we can use DOSKEY
doskey assign=for /f "tokens=1,2 delims=," %a in ("$*") do #for /f "tokens=* delims=" %# in ('"%a"') do #set "%b=%#"
rem -- example --
assign WHOAMI /LOGONID,my-id
echo %my-id%
DOSKEY does accept double quotes as enclosion for arguments so this also is useful for more simple scenarios.
FOR also works well with pipes which can be used for chaining commands (though it is not so good for assigning a variable.
hostname |for /f "tokens=* delims=" %%# in ('more') do #(ping %%#)
Which also can be beautified with macros:
#echo off
:: --- defining chain command macros ---
set "result-as-[arg]:=|for /f "tokens=* delims=" %%# in ('more') do #("
set "[arg]= %%#)"
::: -------------------------- :::
::Example:
hostname %result-as-[arg]:% ping %[arg]%
And for completnes macros for the temp file approach (no doskey definition ,but it also can be easy done.If you have a SSD this wont be so slow):
#echo off
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
set "[[=>"#" 2>&1&set/p "&set "]]==<# & del /q # >nul 2>&1"
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
chcp %[[%code-page%]]%
echo ~~%code-page%~~
whoami %[[%its-me%]]%
echo ##%its-me%##
For /f with another macro:
::::::::::::::::::::::::::::::::::::::::::::::::::
;;set "{{=for /f "tokens=* delims=" %%# in ('" &::
;;set "--=') do #set "" &::
;;set "}}==%%#"" &::
::::::::::::::::::::::::::::::::::::::::::::::::::
:: --examples
::assigning ver output to %win-ver% variable
%{{% ver %--%win-ver%}}%
echo 3: %win-ver%
::assigning hostname output to %my-host% variable
%{{% hostname %--%my-host%}}%
echo 4: %my-host%
cd %windir%\system32\inetsrv
#echo off
for /F "tokens=* USEBACKQ" %%x in (
`appcmd list apppool /text:name`
) do (
echo|set /p= "%%x - " /text:name & appcmd.exe list apppool "%%x" /text:processModel.identityType
)
echo %date% & echo %time%
pause
I most cases, creating a temporary file named after your variable name might be acceptable. (as you are probably using meaningful variables name...)
Here, my variable name is SSH_PAGEANT_AUTH_SOCK
dir /w "\\.\pipe\\"|find "pageant" > %temp%\SSH_PAGEANT_AUTH_SOCK && set /P SSH_PAGEANT_AUTH_SOCK=<%temp%\SSH_PAGEANT_AUTH_SOCK
This question already has answers here:
Assign output of a program to a variable using a MS batch file
(12 answers)
Closed 9 months ago.
Is it possible to set a statement's output of a batch file to a variable, for example:
findstr testing > %VARIABLE%
echo %VARIABLE%
FOR /F "tokens=* USEBACKQ" %%F IN (`command`) DO (
SET var=%%F
)
ECHO %var%
I always use the USEBACKQ so that if you have a string to insert or a long file name, you can use your double quotes without screwing up the command.
Now if your output will contain multiple lines, you can do this
SETLOCAL ENABLEDELAYEDEXPANSION
SET count=1
FOR /F "tokens=* USEBACKQ" %%F IN (`command`) DO (
SET var!count!=%%F
SET /a count=!count!+1
)
ECHO %var1%
ECHO %var2%
ECHO %var3%
ENDLOCAL
In a single line:
FOR /F "tokens=*" %%g IN ('your command') do (SET VAR=%%g)
The command output will be set in %g then in VAR.
More information here: https://ss64.com/nt/for_cmd.html
I found this thread on that there Interweb thing. Boils down to:
#echo off
setlocal enableextensions
for /f "tokens=*" %%a in (
'VER'
) do (
set myvar=%%a
)
echo/%%myvar%%=%myvar%
pause
endlocal
You can also redirect the output of a command to a temporary file, and then put the contents of that temporary file into your variable, likesuchashereby. It doesn't work with multiline input though.
cmd > tmpFile
set /p myvar= < tmpFile
del tmpFile
Credit to the thread on Tom's Hardware.
If you don't want to output to a temp file and then read into a variable, this code stores result of command direct into a variable:
FOR /F %i IN ('findstr testing') DO set VARIABLE=%i
echo %VARIABLE%
If you want to enclose search string in double quotes:
FOR /F %i IN ('findstr "testing"') DO set VARIABLE=%i
If you want to store this code in a batch file, add an extra % symbol:
FOR /F %%i IN ('findstr "testing"') DO set VARIABLE=%%i
A useful example to count the number of files in a directory & store in a variable:
(illustrates piping)
FOR /F %i IN ('dir /b /a-d "%cd%" ^| find /v /c "?"') DO set /a count=%i
Note the use of single quotes instead of double quotes " or grave accent ` in the command brackets. This is cleaner alternative to delims, tokens or usebackq in for loop.
Update 27/8/2021:
Another method is to set errorlevel variable, although many would discourage setting errorlevel on large scripts or when new to cmd flavor of the installed OS variant.
This method works where (return) value to be stored is a 32-bit integer.
eg.
to count the number of files in a directory & store in a variable called errorlevel:
(dir /b /a-d ^| find /v /c "?") | (set /p myVar=& cmd /c exit /b %myVar%)
echo %errorlevel%
81
set /a %errorlevel%+1
82
REM Note: Win CMD arithmetic limit: 2147483647 (32-bit integers)
REM ie. an overflow would continue count at -2147483648
REM and reset again after reaching 2147483647
REM See tests below:
cmd /c exit /b 2147483647
echo %errorlevel%
2147483647
cmd /c exit /b 2147483648
echo %errorlevel%
-2147483648
cmd /c exit /b 2147483649
echo %errorlevel%
-2147483647
Above method can be modified to return encoded strings to be decoded in parent process (within 32 bit limitation).
A 3rd illustration, although of limited use (because the variable is set in child process, not parent) is:
(dir /b /a-d ^| find /v /c "?") | (set /p myVar=& set myVar)
In this case the value of myVar is set to the number of files in the directory
Tested on Win 10 CMD.
To read a file...
set /P Variable=<File.txt
To Write a file
#echo %DataToWrite%>File.txt
note; having spaces before the <> character causes a space to be added at the end of the variable, also
To add to a file,like a logger program,
First make a file with a single enter key in it called e.txt
set /P Data=<log0.log
set /P Ekey=<e.txt
#echo %Data%%Ekey%%NewData%>log0.txt
your log will look like this
Entry1
Entry2
and so on
Anyways a couple useful things
These answers were all so close to the answer that I needed. This is an attempt to expand on them.
In a Batch file
If you're running from within a .bat file and you want a single line that allows you to export a complicated command like jq -r ".Credentials.AccessKeyId" c:\temp\mfa-getCreds.json to a variable named AWS_ACCESS_KEY then you want this:
FOR /F "tokens=* USEBACKQ" %%g IN (`jq -r ".Credentials.AccessKeyId" c:\temp\mfa-getCreds.json`) do (SET "AWS_ACCESS_KEY=%%g")
On the Command Line
If you're at the C:\ prompt you want a single line that allows you to run a complicated command like jq -r ".Credentials.AccessKeyId" c:\temp\mfa-getCreds.json to a variable named AWS_ACCESS_KEY then you want this:
FOR /F "tokens=* USEBACKQ" %g IN (`jq -r ".Credentials.AccessKeyId" c:\temp\mfa-getCreds.json`) do (SET "AWS_ACCESS_KEY=%g")
Explanation
The only difference between the two answers above is that on the command line, you use a single % in your variable. In a batch file, you have to double up on the percentage signs (%%).
Since the command includes colons, quotes, and parentheses, you need to include the USEBACKQ line in the options so that you can use backquotes to specify the command to run and then all kinds of funny characters inside of it.
Some notes and some tricks.
The 'official' way to assign result to a variable is with FOR /F though in the other answers is shown how a temporary file can be used also.
For command processing FOR command has two forms depending if the usebackq option is used. In the all examples below the whole output is used without splitting it.
FOR /f "tokens=* delims=" %%A in ('whoami') do #set "I-Am=%%A"
FOR /f "usebackq tokens=* delims=" %%A in (`whoami`) do #set "I-Am=%%A"
and if used directly in the console:
FOR /f "tokens=* delims=" %A in ('whoami') do set "I-Am=%A"
FOR /f "usebackq tokens=* delims=" %A in (`whoami`) do set "I-Am=%A"
%%A is a temporary variable available only on the FOR command context and is called token.The two forms can be useful in case when you are dealing with arguments containing specific quotes. It is especially useful with REPL interfaces of other languages or WMIC.
Though in both cases the expression can be put in double quotes and it still be processed.
Here's an example with python (it is possible to transition the expression in the brackets on a separate line which is used for easier reading):
#echo off
for /f "tokens=* delims=" %%a in (
'"python -c ""print("""Message from python""")"""'
) do (
echo processed message from python: "%%a"
)
To use an assigned variable in the same FOR block check also the DELAYED EXPANSION
And some tricks
To save yourself from writing all the arguments for the FOR command you can use MACRO for assigning the result to variable:
#echo off
::::: ---- defining the assign macro ---- ::::::::
setlocal DisableDelayedExpansion
(set LF=^
%=EMPTY=%
)
set ^"\n=^^^%LF%%LF%^%LF%%LF%^^"
::set argv=Empty
set assign=for /L %%n in (1 1 2) do ( %\n%
if %%n==2 (%\n%
setlocal enableDelayedExpansion%\n%
for /F "tokens=1,2 delims=," %%A in ("!argv!") do (%\n%
for /f "tokens=* delims=" %%# in ('%%~A') do endlocal^&set "%%~B=%%#" %\n%
) %\n%
) %\n%
) ^& set argv=,
::::: -------- ::::::::
:::EXAMPLE
%assign% "WHOAMI /LOGONID",result
echo %result%
the first argument to the macro is the command and the second the name of the variable we want to use and both are separated by , (comma). Though this is suitable only for straight forward scenarios.
If we want a similar macro for the console we can use DOSKEY
doskey assign=for /f "tokens=1,2 delims=," %a in ("$*") do #for /f "tokens=* delims=" %# in ('"%a"') do #set "%b=%#"
rem -- example --
assign WHOAMI /LOGONID,my-id
echo %my-id%
DOSKEY does accept double quotes as enclosion for arguments so this also is useful for more simple scenarios.
FOR also works well with pipes which can be used for chaining commands (though it is not so good for assigning a variable.
hostname |for /f "tokens=* delims=" %%# in ('more') do #(ping %%#)
Which also can be beautified with macros:
#echo off
:: --- defining chain command macros ---
set "result-as-[arg]:=|for /f "tokens=* delims=" %%# in ('more') do #("
set "[arg]= %%#)"
::: -------------------------- :::
::Example:
hostname %result-as-[arg]:% ping %[arg]%
And for completnes macros for the temp file approach (no doskey definition ,but it also can be easy done.If you have a SSD this wont be so slow):
#echo off
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
set "[[=>"#" 2>&1&set/p "&set "]]==<# & del /q # >nul 2>&1"
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
chcp %[[%code-page%]]%
echo ~~%code-page%~~
whoami %[[%its-me%]]%
echo ##%its-me%##
For /f with another macro:
::::::::::::::::::::::::::::::::::::::::::::::::::
;;set "{{=for /f "tokens=* delims=" %%# in ('" &::
;;set "--=') do #set "" &::
;;set "}}==%%#"" &::
::::::::::::::::::::::::::::::::::::::::::::::::::
:: --examples
::assigning ver output to %win-ver% variable
%{{% ver %--%win-ver%}}%
echo 3: %win-ver%
::assigning hostname output to %my-host% variable
%{{% hostname %--%my-host%}}%
echo 4: %my-host%
cd %windir%\system32\inetsrv
#echo off
for /F "tokens=* USEBACKQ" %%x in (
`appcmd list apppool /text:name`
) do (
echo|set /p= "%%x - " /text:name & appcmd.exe list apppool "%%x" /text:processModel.identityType
)
echo %date% & echo %time%
pause
I most cases, creating a temporary file named after your variable name might be acceptable. (as you are probably using meaningful variables name...)
Here, my variable name is SSH_PAGEANT_AUTH_SOCK
dir /w "\\.\pipe\\"|find "pageant" > %temp%\SSH_PAGEANT_AUTH_SOCK && set /P SSH_PAGEANT_AUTH_SOCK=<%temp%\SSH_PAGEANT_AUTH_SOCK
I want to count the no of lines in a text file and then the value has to be stored into a environment variable. The command to count the no of lines is
findstr /R /N "^" file.txt | find /C ":"
I refered the question How to store the result of a command expression in a variable using bat scripts?
Then I tried,
set cmd="findstr /R /N "^" file.txt | find /C ":" "
I am getting the error message,
FIND: Parameter format not correct
How could i get rid of this error.
There is a much simpler way than all of these other methods.
find /v /c "" filename.ext
Holdover from the legacy MS-DOS days, apparently. More info here: https://devblogs.microsoft.com/oldnewthing/20110825-00/?p=9803
Example use:
adb shell pm list packages | find /v /c ""
If your android device is connected to your PC and you have the android SDK on your path, this prints out the number of apps installed on your device.
You could use the FOR /F loop, to assign the output to a variable.
I use the cmd-variable, so it's not neccessary to escape the pipe or other characters in the cmd-string, as the delayed expansion passes the string "unchanged" to the FOR-Loop.
#echo off
cls
setlocal EnableDelayedExpansion
set "cmd=findstr /R /N "^^" file.txt | find /C ":""
for /f %%a in ('!cmd!') do set number=%%a
echo %number%
Inspired by the previous posts,
a shorter way of doing so:
CMD.exe
C:\>FINDSTR /R /N "^.*$" file.txt | FIND /C ":"
The number of lines
Try it. It works in my console.
EDITED:
(the "$" sign removed)
FINDSTR /R /N "^.*" file.txt | FIND /C ":"
$ reduces the number by 1 because it is accepting the first row as Field name and then counting the number of rows.
Try this:
#Echo off
Set _File=file.txt
Set /a _Lines=0
For /f %%j in ('Find "" /v /c ^< %_File%') Do Set /a _Lines=%%j
Echo %_File% has %_Lines% lines.
It eliminates the extra FindStr and doesn't need expansion.
- edited to use ChrisJJ's redirect suggestion. Removal of the TYPE command makes it three times faster.
#Tony: You can even get rid of the type %file% command.
for /f "tokens=2 delims=:" %%a in ('find /c /v "" %_file%') do set /a _Lines=%%a
For long files this should be even quicker.
I usually use something more like this
for /f %%a in (%_file%) do (set /a Lines+=1)
for /f "usebackq" %A in (`TYPE c:\temp\file.txt ^| find /v /c "" `) do set numlines=%A
in a batch file, use %%A instead of %A
The perfect solution is:
FOR /F %%i IN ('TYPE "Text file.txt" ^| FIND /C /V ""') DO SET Lines=%%i
I found this solution to work best for creating a log file that maintains itself:
setlocal enabledelayedexpansion
SET /A maxlines= 10
set "cmd=findstr /R /N "^^" "filename.txt" | find /C ":""
for /f %%a in ('!cmd!') do set linecount=%%a
GOTO NEXT
:NEXT
FOR /F %%A IN ("filename.txt") DO (
IF %linecount% GEQ %maxlines% GOTO ExitLoop
echo %clientname% %Date% %Time% >> "filename.txt")
EXIT
:ExitLoop
echo %clientname% %Date% %Time% > "filename.txt"
EXIT
Environmental variables included are %clientname% the computername of the remote client %Date% is the current date and %Time% the current time. :NEXT is called after getting the number of lines in the file. If the file line count is greater than the %maxlines% variable it goes to the :EXITLOOP where it overwrites the file, creating a new one with the first line of information. if it is less than the %maxlines% variable it simply adds the line to the current file.
You don't need to use find.
#echo off
set /a counter=0
for /f %%a in (filename) do set /a counter+=1
echo Number of lines: %counter%
This iterates all lines in the file and increases the counter variable by 1 for each line.
The :countLines subroutine below accepts two parameters: a variable name; and a filename. The number of lines in the file are counted, the result is stored in the variable, and the result is passed back to the main program.
The code has the following features:
Reads files with Windows or Unix line endings.
Handles Unicode as well as ANSI/ASCII text files.
Copes with extremely long lines.
Isn’t fazed by the null character.
Raises an error on reading an empty file.
Counts beyond the Batch max int limit of (31^2)-1.
#echo off & setLocal enableExtensions disableDelayedExpansion
call :countLines noOfLines "%~1" || (
>&2 echo(file "%~nx1" is empty & goto end
) %= cond exec =%
echo(file "%~nx1" has %noOfLines% line(s)
:end - exit program with appropriate errorLevel
endLocal & goto :EOF
:countLines result= "%file%"
:: counts the number of lines in a file
setLocal disableDelayedExpansion
(set "lc=0" & call)
for /f "delims=:" %%N in ('
cmd /d /a /c type "%~2" ^^^& ^<nul set /p "=#" ^| (^
2^>nul findStr /n "^" ^&^& echo(^) ^| ^
findStr /blv 1: ^| 2^>nul findStr /lnxc:" "
') do (set "lc=%%N" & call;) %= for /f =%
endlocal & set "%1=%lc%"
exit /b %errorLevel% %= countLines =%
I know it looks hideous, but it covers most edge-cases and is surprisingly fast.
Just:
c:\>(for /r %f in (*.java) do #type %f ) | find /c /v ""
Font: https://superuser.com/questions/959036/what-is-the-windows-equivalent-of-wc-l
One nice surprise is for one who has git bash on his windows: just plain old linux wc -l <filename> will works for you there
In the below code, the variable name are SalaryCount and TaxCount
#ECHO OFF
echo Process started, please wait...
for /f %%C in ('Find /V /C "" ^< "D:\Trial\Salary.txt"') do set SalaryCount=%%C
echo Salary,%SalaryCount%
for /f %%C in ('Find /V /C "" ^< "D:\Trial\Tax.txt"') do set TaxCount=%%C
echo Tax,%TaxCount%
Now if you need to output these values to a csv file, you could use the below code.
#ECHO OFF
cd "D:\CSVOutputPath\"
echo Process started, please wait...
echo FILENAME,FILECOUNT> SUMMARY.csv
for /f %%C in ('Find /V /C "" ^< "D:\Trial\Salary.txt"') do set Count=%%C
echo Salary,%Count%>> SUMMARY.csv
for /f %%C in ('Find /V /C "" ^< "D:\Trial\Tax.txt"') do set Count=%%C
echo Tax,%Count%>> SUMMARY.csv
The > will overwrite the existing content of the file and the >> will append the new data to existing data. The CSV will be generated in D:\CSVOutputPath
You can pipe the output of type into find inside the in(…) clause of a for /f loop:
for /f %%A in ('
type "%~dpf1" ^| find /c /v ""
') do set "lineCount=%%A"
But the pipe starts a subshell, which slows things down.
Or, you could redirect input from the file into find like so:
for /f %%A in ('
find /c /v "" ^< "%~dpf1"
') do set "lineCount=%%A"
But this approach will give you an answer 1 less than the actual number of lines if the file ends with one or more blank lines, as teased out by the late foxidrive in counting lines in a file.
And then again, you could always try:
find /c /v "" example.txt
The trouble is, the output from the above command looks like this:
---------- EXAMPLE.TXT: 511
You could split the string on the colon to get the count, but there might be more than one colon if the filename had a full path.
Here’s my take on that problem:
for /f "delims=" %%A in ('
find /c /v "" "%~1"
') do for %%B in (%%A) do set "lineCount=%%B"
This will always store the count in the variable.
Just one last little problem… find treats null characters as newlines. So if sneaky nulls crept into your text file, or if you want to count the lines in a Unicode file, this answer isn’t for you.
You can also try
set n=0 & for /f "tokens=*" %a in (text.txt) do set/a n=!n!+1
echo !n!
You can also mark with a wildcard symbol * to facilitate group files to count.
Z:\SQLData>find /c /v "" FR_OP133_OCCURENCES_COUNT_PER_DOCUMENTS_*.txt
Result
---------- FR_OP133_OCCURENCES_COUNT_PER_DOCUMENTS_AVIFRS01_V1.TXT: 2041
---------- FR_OP133_OCCURENCES_COUNT_PER_DOCUMENTS_AVIOST00_V1.TXT: 315938
---------- FR_OP133_OCCURENCES_COUNT_PER_DOCUMENTS_AVIFRS00_V1.TXT: 0
---------- FR_OP133_OCCURENCES_COUNT_PER_DOCUMENTS_CNTPTF00_V1.TXT: 277