How to base conditional statements on existence of a variable substring - windows

I'm writing a simple batch file to create new, user-defined files.
My question is how to accept input for the extension with or without a period, and not yield a double-period (ie. name1..txt).
I also want to avoid having to print instructions for them to include/not include it. Thanks for helping!
My approach is below. I want to look for a period at the beginning of the "extension" variable, ext, and run the appropriate FOR loop to create files.
setlocal enabledelayedexpansion
set num=
set name=
set ext=
:setnum
set /P "num=Number of files to create?: "
If not defined num Echo.&Echo You must enter a number to continue...&goto:setnum
:setname
set /P "name=Enter "root" name of file:"
If not defined name echo.&Echo You must enter the name of your new files to continue...&goto:setname
:setext
set /P "ext=What will be the file extension?:"
If not defined ext echo.&Echo You must enter an extension to continue...&goto:setext
pause
%ext:~0,1% | FINDSTR /R "[.]" && pause & goto:extNoDot
%ext:~0,1% | FINDSTR /R "[.]" || pause & goto:extYesDot
:extNoDot
for /L %%a in (1,1,%num%) do (echo %%a >> !name!%%a%ext%)
goto:eof
:extYesdot
for /L %%a in (1,1,%num%) do (echo %%a >> !name!%%a.%ext%)
goto:eof
:eof
EXIT /b

You don't actually state what's wrong with your current code. Without testing it, I can see that these two lines must be giving you problems:
%ext:~0,1% | FINDSTR /R "[.]" && pause & goto:extNoDot
%ext:~0,1% | FINDSTR /R "[.]" || pause & goto:extYesDot
This is because you've got %ext:~0,1% at the beginning of a line as if it were a command. What you seem to be trying to do is pipe these to the FINDSTR command. Therefore you need to echo them:
echo %ext:~0,1% | FINDSTR /R "[.]" && pause & goto:extNoDot
echo %ext:~0,1% | FINDSTR /R "[.]" || pause & goto:extYesDot
However, using an external command here is overkill. You should do the following instead:
rem Remove any optional dot at the start
if "%ext:~0,1%" == "." set "ext=%ext:~1%"
And then just carry on as if ext never had a dot in the first place (no need for separate nodot and yesdot labels).

Related

Batch file searching for multiple file formats in FOR loop

I'm coding a batch file that should search two folder paths for multiple file extensions and list them in a text file. Currently I'm trying to use a FOR loop with a list of file extensions (*.doc, *.docx, etc). I believe the file is erroring out because of the "*" character but I don't know how to correct this.
I've tried to straight list them: FOR %%G IN (*.one,*.mht,*.onepkg). I've tried quote marks: FOR %%G IN ("*.one","*.mht","*.onepkg"). I've tried carets: FOR %%G IN (^^*.one,^^*.mht,^^*.onepkg).
Here's my code:
set outputfilepath=d:\output.txt
FOR %%G IN ("*.one","*.mht","*.onepkg") DO (
echo Searching for %%G files
dir "C:\%%G" /s /b >> "%outputfilepath%"
Rem Add 2 blank lines between next search
echo. >> "%outputfilepath%"
echo. >> "%outputfilepath%" )
Nothing gets output to my text file.
Any help is appreciated.
#ECHO Off
SETLOCAL
set "outputfilepath=u:\output.txt"
(
FOR %%G IN (one,mht,onepkg) DO (
echo Searching for %%G files>con
dir ".\*.%%G" /s /b |FINDSTR /i /e /L ".%%G"
Rem Add 2 blank lines between next search
echo.
echo.
)
)> "%outputfilepath%"
GOTO :EOF
Please note that I've changed drivenames to suit my system.
simply for meta in extensionlist then add the * in the dir command. Filter the dir output using findstr to ensure that only names matching /e at the end /L the literal ".%%G" are shown.
Also by enclosing the enitre for command in parentheses, you can send all stdout text (which would normally appear on the console) to the file. > naturally means create-file-anew. >> to append if that's your preference.
The >con appended to the Searching... echo overrides the redirection and specifically sends the text from that echo to the console.
I really like the existing suggestions but this is a different approach. I find that this style reads more like code.
This may seem very complicated but...
This technique can be used to loop over parameters for ANYTHING to include ones passed via the command line.
The logic of looping through passed parameters is isolated to its own function (enumerate_search_types).
The logic of what you are going to do with each parameter is isolated to its own function (search_for_search_type).
This might make it easier for some, too complicated for others.
#echo off
:: The parameters we are working with...
set outputfilepath=d:\output.txt
set starting_path=c:\
set search_types="*.one" "*.mht" "*.onepkg"
pushd "%starting_path%"
call :enumerate_search_types %search_types%
popd
goto :EOF
:: ---------------------------------------------------------------
:enumerate_search_types
set "current_param=%~1"
if "%current_param%"=="" goto :EOF
call :search_for_search_type "%current_param%"
shift /1
goto :enumerate_search_types
:: ---------------------------------------------------------------
:search_for_search_type
set "current_search_type=%1"
set "had_output=false"
echo Searching for %current_search_type% files
for /f "delims=" %%f in ('dir /s /b %current_search_type% 2^>NUL') do set had_output=true&& echo %%f >> "%outputfilepath%"
:: If the dir command didn't produce anything, don't add the blank lines
if "false"=="%had_output%" goto :EOF
echo. >> "%outputfilepath%"
echo. >> "%outputfilepath%"
goto :EOF
Short answer if you refer to only the file extension.
In order to use different extension in a for loop:
FOR %%G IN (one,mht,onepkg) do [command]

FINDSTR: help understanding results. Returns nothing, but no error when there is one expected?

I am writing a batch file. part of the program will compare the list of files in a 'source' folder. With the contents of a list in a text file.
I loop through each file in the folder, and search for its filename in the text file using FINDSTR
Everything works until there is a filename in the source folder that doesnt exist in the text file.
the findstr code:
for /f %%o in ('findstr %name% old.txt') do (
echo o=%%o >> result.txt
if %%o==%name% (
echo %name% exists
) ELSE (
echo %name% does not exists
)
)
Again, the problem occurs when FINDSTR searches for a filename that is not in the text file.
when it reaches that point it outputs the variable %%o as being '%o' and echos nothing. So it sends nothing to the results.txt.
This doesnt trigger an ERRORLEVEL change but also will not echo anything. I have tried outputing the errorlevels but they are also empty. I just dont understand what FINDSTR is doing in this instance.
the FULL batch file: (its my first one. forgive any mistakes)
::return the raw (/b) list of files
FORFILES /p %~dp0source\ /s /m "*.cr2" /C "cmd /c echo #path" > new.txt
::pull file path for each file and send to subroutine
for /f %%n in ('FORFILES /p %~dp0source\ /s /m "*.cr2" /C "cmd /c echo #path"') do (
call :dequote %%n
)
::subroutine for removing quotes
::and returning the filename, extension, and path
:dequote
set fullPath=%~f1
set fileName=%~n1
set fileExt=%~x1
set filePath=%~dp1
set name=%fileName%& set npath=%filePath%& set ext=%fileExt%& set fpath=%fullPath%
echo %fpath%
echo %npath%
echo %name%
echo %ext%
for /f %%o in ('findstr %name% old.txt') do (
echo o=%%o >> result.txt
if %%o==%name% (
echo %name% exists
) ELSE (
echo %name% does not exists
)
)
This only happens on the last filename sent to findstr. Any suggestions or direction would be very appreciated. Ive tried and read everything I can get my hands on.
Thank You for your time.
UPDATE: 9-9-15
Here is the working final batch file i created using the help on this page. It creates a hotfolder that will edit any new files added to it until you stop the script from running:
:start
:: return the raw (/b) list of files and full path to source text
FORFILES /p %~dp0source\ /s /m "*.cr2" /C "cmd /c echo #path" > source.txt
IF %ERRORLEVEL% EQU 1 goto :start
::join new and old data, return only what is shared in common (/g)
findstr /I /L /G:"source.txt" "output.txt" > found.txt
IF %ERRORLEVEL% EQU 1 copy /y source.txt notFound.txt
::join found file names and source filenames, return those that do not have a match
findstr /I /L /V /G:"found.txt" "source.txt" >> notFound.txt
IF %ERRORLEVEL% EQU 2 echo error no match
::for each line of notFound.txt, dequote and break apart
for /f %%n in (notFound.txt) do (
echo n=%%n
call :dequote %%n
)
:dequote
set fullPath=%~f1
set fileName=%~n1
set fileExt=%~x1
set filePath=%~dp1
set name=%fileName%& set npath=%filePath%& set ext=%fileExt%& set fpath=%fullPath%
echo %fpath%
echo %npath%
echo %name%
echo %ext%
cd %nPath%
if NOT [%1]==[] (
echo converted %name%
convert -negate -density 600 -colorspace gray flatField.cr2 %name%%ext% -compose Divide -composite %name%.tif
move %name%.tif %~dp0output
cd %~dp0
del notFound.txt
copy /y source.txt output.txt
) ELSE (
echo end of batch else
cd %~dp0
)
Loop variables must be referenced with %% in a batch file because percent sign has a special meaning and must be therefore escaped with another percent sign in a batch file to specify it literally. This is the reason why on running the batch file with echo on in a command prompt window results in getting %%o in the batch file displayed as %o on execution.
Command FOR as used in
for /f %%o in ('findstr %name% old.txt') do
processes the output written to stdout by the called command findstr. But findstr does not write anything to standard output when it searched for one or more strings in a file and could not find any matching string in any line of the file.
So command for can't process anything and therefore none of the commands after do are processed at all in this case.
Assuming the list file contains only file names without path, the following commented batch file can be used to get with 1 execution of command dir and just 1 or 2 executions of console application findstr the two lists containing the file names in folder being found and being not found in the list file. The batch file is written for not producing empty files.
#echo off
setlocal
set "ListFile=C:\Temp\List.txt"
if not exist "%ListFile%" goto NoListFile
set "SourceFolder=C:\Temp\Test"
if not exist "%SourceFolder%\*" goto NoSourceFolder
set "AllFileNames=%TEMP%\AllFileNames.txt"
set "FoundFileNames=%TEMP%\FoundFileNames.txt"
set "NotFoundFileNames=%TEMP%\NotFoundFileNames.txt"
rem Get alphabetic list of files in source folder without path.
dir /A /B /ON "%SourceFolder%" >"%AllFileNames%"
rem Find all file names in list file with a case-insensitive
rem search matching completely a file name in list file and
rem output the found file names to another list file.
%SystemRoot%\system32\findstr.exe /I /L /X "/G:%AllFileNames%" "%ListFile%" >"%FoundFileNames%"
if errorlevel 1 goto NoFileNameFound
rem Find all file names with a case-insensitive search found
rem before in all file names list and output the lines not
rem containing one of the file names to one more list file.
%SystemRoot%\system32\findstr.exe /I /L /V "/G:%FoundFileNames%" "%AllFileNames%" >"%NotFoundFileNames%"
if errorlevel 1 goto AllFileNamesFound
rem Some file names are found in list file and others not.
del "%AllFileNames%"
goto :EndBatch
:NoFileNameFound
move /Y "%AllFileNames%" "%NotFoundFileNames%"
del "%FoundFileNames%"
goto EndBatch
:AllFileNamesFound
del "%AllFileNames%"
del "%NotFoundFileNames%"
goto EndBatch
:NoListFile
echo %~f0:
echo Error: No list file %ListFile%
goto EndBatch
:NoSourceFolder
echo %~f0:
echo Error: No folder %SourceFolder%
:EndBatch
endlocal
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
del /?
dir /?
findstr /?
goto /?
if /?
move /?
set /?
This is a method to give you a list of filenames which don't exist in the file.txt
#echo off
cd /d "c:\folder\to\check"
for %%a in (*) do findstr /i "%%~nxa" "file.txt" >nul || echo "%%a" is missing
pause
It uses %%~nxa instead of %%a in case subdirectories are used at some point.

Windows Batch for Pipe out Variable

How can i get the value to myVar attribute to use in the other part of code.
set myVar =
ECHO "httpsxxxxxx_cmidxxxxx_2014-04-12_14-54-53_abc3654.xml" | find /i "2014-04-12" | (set /p myVar= & set myVar)
ECHO myvar "%myVar%" /**** This is empty String**********/
Maybe what you are asking is:
This line
echo "someValue" | find /i "Value" | (set /p myVar= & set myVar)
will show you the variable has been set. echo command send its output to find command, and the output of it is piped into the set /p, assigning the value to the variable, value that the set will show in console.
Then the following line
echo myvar "%myVar%"
shows no data. Why?
When cmd finds a pipe definition, with data flowing from one program to other, each of the elements in the pipe is a separate process with its own copy of the environment block of the parent process (where the batch file is running), so the set /p myVar= & set myVar is not running inside the same instance as the batch file. The variable is set in another process, and as the changes are made in a copy of the environment, they are not visible from the parent process.
Try like this :
#echo off
for /f "delims=" %%a in ('ECHO "httpsxxxxxx_cmidxxxxx_2014-04-12_14-54-53_abc3654.xml" ^| find /i "2014-04-12"') do set MyVar=%%a
echo %MyVar%
I don't anderstand what you are trying to do.
But assuming that you're looking for a .xml file who contain the string "2014-04-12" in a directory. The code will be :
#echo off
for /f "delims=" %%a in ('dir /b/a-d *.xml ^| find /i "2014-04-12"') do set MyVar=%%a
Annotating your code:
1: set myVar =
2: ECHO "httpsxxxxxx_cmidxxxxx_2014-04-12_14-54-53_abc3654.xml" | find /i "2014-04-12" | (set /p myVar= & set myVar)
3: ECHO myvar "%myVar%" /**** This is empty String**********/
On line 1:, there is a space between myVar and =. This is incorrect. CMD.EXE considers that there is a variable called "myVar ". Change this to "myVar".
On line 2:, I guess that you intended to use TYPE to output the file "httpsxxxxxx_cmidxxxxx_2014-04-12_14-54-53_abc3654.xml". In any case you could have written this, instead:
FIND /I "2014-04-12" httpsxxxxxx_cmidxxxxx_2014-04-12_14-54-53_abc3654.xml

How to store the result of a command to a variable in batch?

I'm tryng to make a .bat file to add blank lines to a text file, based on the amount of lines that match a condition. This is what a have:
#echo
SET /a maxLineas = 50
SET cantLineasDetalle="type texto.txt | find /i /c "D01" "
SET /a cantLineasAgregar = %maxLineas% - %cantLineasDetalle%
:loop
echo. >> texto.txt
set /a cantLineasAgregar-=1
if %cantLineasAgregar% GTR 0 goto loop
The trouble is that var "cantLineasDetalle" isn't storing the value that I want it to do.
How do I asign the result of the execution of 'type texto.txt | find /i /c "D01"' to a variable?
Thanks in advance,
Esteban.
As the previous answer indicated, you use a FOR /F loop to store the results of a command into a variable.
find "search" <file can be significantly more efficient than type file | find "search" if the file is large.
When executed within a FOR /F IN() clause, all special characters must be either quoted or escaped. In your case the pipe will need to be escaped, or if you take my suggestion, the < will need to be escaped.
echo. >>file will append a line with a space to the file. Also, it is safer to use echo( instead of echo., but you will probably never run into a problem with echo. To get a blank line without a space use echo(>>file
When using SET /A to do math, you can refer to variables directly without enclosing them in percents. It also works with the percents.
Lastly, it is much more efficient to append the lines within a FOR /L loop instead of using a GOTO loop.
#echo off
set /a maxLineas=50
for /f %%N in('find /i /c:"D01" ^<texto.txt') do set /a cantLineasDetalle=%%N
set /a cantLineasAgregar=maxLineas-cantLineasDetalle
for /l %%N in (1 1 %cantLineasAgregar%) do echo(>>texto.txt
The entire script could be compressed to the following (maxLineas is now 0 based)
#echo off
set /a maxLineas=50-1
for /f %%N in('find /i /c:"D01" ^<texto.txt') do for /l %%I in (%%N 1 %maxLineas%) do echo(>>texto.txt
for /f %%i in ('type texto.txt | find /i /c "D01"') do SET cantLineasDetalle = %%i

How to count no of lines in text file and store the value into a variable using batch script?

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

Resources