Windows batch: for and findstr to include and exclude from results - windows

I have a list of strings I'm iterating over, and I need to exclude certain ones and include others in the subsequent processing.
Something like this:
listmystuff | for /F "usebackq" %%D in (`findstr /r "something*" ^| findstr /v "but not thisstuff"`) do interestingthing
This is wrong. What does work is having one of the findstr's but not both.
What would be right?

You want a pipe |, not conditional command concatenation &&. The pipe character must be escaped.
You only want one set of single quotes around the entire commmand. (or backticks if using usebackq option)
You need to double the percents if used within a batch file.
Your initial FINDSTR needs a filespec to search (or else data piped into it)
for /f "options" %%D in ('findstr "something" fileSpec ^| findstr /v "butNotThis"') do myThing

Give this a run with your show databases command. This is the usual way to use a for command to parse data.
for /F "delims=" %%D in ('show databases ^| findstr /r "something*" ^| findstr /v /c:"but not thisstuff" ') do echo %%D

Related

Output all results of findstr to seperate txt files

Currently my code is: findstr Starfy ./List.txt > result.txt
My result.txt is
3841 - Legendary Starfy, The (USA).zip
x166 - Legendary Starfy, The (USA) (Demo) (Kiosk).zip
However, I want result 1 and result 2 to have their own seperate files, so it would look like:
result1.txt > 3841 - Legendary Starfy, The (USA).zip
result2.txt > x166 - Legendary Starfy, The (USA) (Demo) (Kiosk).zip
I'm unsure how to make this work, and would love if someone is able to help point me in the right direction.
This works:
#echo off
for /F "tokens=1* delims=:" %%a in ('findstr "Starfy" .\List.txt ^| findstr /N "^"') do >result%%a.txt echo %%b
Just pass your original results into another instance of findstr /N command that add line numbers. After that, separate the number and the line in a for /F command and output each line to its corresponding numbered file...
for /f "tokens=1,* delims=:" %%S in ('findstr /i "echo" "%~f0"^|findstr /n /i "echo" ') do ECHO %%T>"U:\moreresults\result%%S.txt"
The command quoted in parentheses finds a string (in this case, echo) ignoring case (/i) within the file "%~f0" (this batch file, which contains a heap of standard code I use for testing). This is passed to another instance of findstr, this time looking for the same string, but numbering the lines (as serialnumber:linetext).
The resultant text is tokenised using : as a separator, so %%S receives the serialnumber and %%T receives the rest of the line (token *). Then simply build the result filename using %%S and write the text part of the line to it.
The caret is used to escape the pipe so that cmd knows that the pipe is part of the command-to-be-executed, not of the for command.
Assuming that you do not already have files in the location you're outputting your results, which could alalready be named using that intended naming scheme, then something like this may suit you:
#Echo Off
SetLocal EnableExtensions EnableDelayedExpansion
Set "i=0"
For /F Delims^=^ EOL^= %%G In (
'%SystemRoot%\System32\find.exe /I "Starfy" 0^<".\List.txt" 2^>NUL'
) Do (
Set /A i += 1
1>"Result!i!.txt" Echo %%G
)
Please note that I used find.exe instead of findstr.exe simply because your example used a simple string containing a series of alphabetic only characters. Feel free to change it to '%SystemRoot%\System32\findstr.exe /LIC:"Starfy" ".\List.txt" 2^>NUL', or similar, should you require a more specialized matching mechanism.

Exclude all rows of a file of key strings contained in some other file

I am trying to write a Windows batch file where there are two files in concern.
The first file all.err contains logs of failed test cases like below:
`CHECK:FAIL,"It should have been DEST`"
`CHECK:FAIL,"It should have been XYZA`"
`CHECK:FAIL,"It should have been PRTS`"
`CHECK:FAIL,"It should have been ABCD`"
.....................................
We have another file exclude.txt which stores some string per line like:
XYZA
ABCD
I am trying to write a Windows batch script which can list all lines from all.err that do not contain any word in exclude.txt.
I am not able to understand how this can be implemented - any idea?
There is just one Windows command line necessary for this task as written by SomethingDark:
%SystemRoot%\System32\findstr.exe /I /L /V /G:exclude.txt all.err
The help output on running in a Windows command prompt window findstr /? explains the parameters /I (case-insensitive), /L (search strings are literal strings), /V (inverse matching output), /G (get search strings from file).
When using a for loop, you need to go through exclusions file first, get each line, then exclude these meta viariables from your search in the log file:
#echo off
for /f "delims=" %%i in (exclude.txt) do (
for /f "delims=" %%b in ('type all.log ^| findstr /V "%%i"') do echo(%%b
)
The first do code block is not needed, I simply posted it like that for readability, it simply can be:
#echo off
for /f "delims=" %%i in (exclude.txt) do for /f "delims=" %%b in ('type all.log ^| findstr /V "%%i"') do echo(%%b

Truncate filename after a multiple of a special character in windows batch script?

I want to remove the part of a filename after the third "_" from thousand of files. The structure after the third "_" varies and contains "_" in some cases. The length of the first part varies so I can't just remove the first 15 characters. The result should be unique.
The filenames look like this:
00_TEXT_=Text00._AA1234L_AA1_1.pdf
00_TEX_=Text00._AA1234L_AA1_2.pdf
00_TEXT_=TextText00._DD2023A.pdf
00_TEXT_=Text00._AA2345L_BB1_1.pdf
00_TEXT_=Text00._AA2345L_BB1_2.pdf
The result should look like this:
AA1234L_AA1_1.pdf
AA1234L_AA1_2.pdf
DD2023A.pdf
AA2345L_BB1_1.pdf
AA2345L_BB1_2.pdf
Any idea why this is not working:
#echo off
setlocal enabledelayedexpansion
set deletestring=*_*_*_
for /f "delims==" %%F in ('dir /b ^| find "%deletestring%"') do (
set oldfilename=%%F
set newfilename=!oldfilename:%deletestring%=!
Ren "!oldfilename!" "!newfilename!"
)
I was able to get it working with this:
#echo off
setlocal enabledelayedexpansion
set deletestring=*_*_*_*
for /f "tokens=1,2,3,* delims=_" %%F in ('dir /b "%deletestring%"') do (
Ren "%%F_%%G_%%H_%%I" "%%I"
)
endlocal
Note that enabledelayedexpansion isn't really needed in the above.
Alternately, you could do this as a single line (no batch file needed):
for /f "tokens=1,2,3,* delims=_" %F in ('dir /b "*_*_*_*"') do Ren "%F_%G_%H_%I" "%I"
The idea is to simply split the matching filenames apart by underscores and then reconstruct the names during the rename process (%%F_%%G_%%H_%%I gives the original file name when going through the loop). Then rename the file to everything after the 3rd underscore, which is the %%I value.
Your FINDSTR search is wrong - a string of any characters (wildcard) is .*, not *.
Variable find/replace does not support wildcards, except for the !var:*search=! syntax that replaces everthing up until the first occurrence of "search".
There is no need for FINDSTR, all you need is DIR with normal wildcard masking.
You can use FOR /F to parse the name into tokens. I use two loops - the first to get the entire name, and the second to parse out the portion after the 3rd _.
The following should work:
#echo off
for /f "eol=: delims=" %%A in (
'dir /b /a-d *_*_*_*'
) do for /f "tokens=3* delims=_" %%B in ("%%A") do ren "%%A" "%%C"
Or you could use my jren.bat utility that renames files using regular expression replacement. It is a hybrid JScript/batch script that runs natively on any Windows machine from XP onward.
jren "^(.*?_){3}" ""
Use CALL JREN if you put the command within another batch script.

FINDSTR and skipping folders

I'm new to Windows batch programming and to Stack Overflow, so please forgive me if I ask anything that's blatantly obvious to you seasoned, talented folks. I'm using Windows batch (.bat) to find files containing a certain string using findstr. However, I'm trying to skip certain folders within a directory.
setlocal EnableDelayedExpansion
set basedir=C:\folder
for /f %%g in ('dir /a:-h /b %basedir% ^| findstr /v "Projects" ^| findstr /v "Archive"') do (
findstr /i /m /s /c:"request" %basedir%\%%g *.* > %basedir%\Projects\list.txt
)
When I look in list.txt, the file output from findstr, I find that the folders I told it not to search were searched. That is, the output looks like this:
C:\folder\somefile.rtf
C:\folder\Requests\anotherfile.rtf
C:\folder\Projects\dontsearchme.txt
C:\folder\Archive\dontsearchmeeither.txt
C:\folder\Archive\Projects\dontsearchme.txt
If it had worked correctly, only C:\folder\somefile.rtf and C:\folder\Requests\anotherfile.rtf would have been included in list.txt. To test the looping code, I used the following:
setlocal EnableDelayedExpansion
set basedir=C:\folder
for /f %%g in ('dir /a:-h /b %basedir% ^| findstr /v "Projects" ^| findstr /v "Archive"') do (
echo %basedir%\%%g
)
That code works as desired; it skips the Projects and Archive folders. I assume that the problem has something to do with how I'm calling findstr but I haven't been able to identify the error of my ways. Any insight would be much appreciated!
Thanks so much!
-Alex
The FINDSTR /S option is causing it to search all folders, thus bypassing the intent of your FOR loop.
Stephan did successfully diagnose another problem with your code regarding redirection using overwrite instead of append mode.
But there is a much simpler method to get your desired result. Simply let FINDSTR search all folders, and pipe the result to an additional FINDSTR to remove results containing the unwanted folders. Since there is no loop, you can safely use owverwrite mode for redirection.
findstr /misl request "%basedir%\*" | findstr /liv "\\projects\\ \\archive\\" >"%basedir%\Projects\list.txt"
EDIT
The above simple solution will waste time searching folders that will later get excluded. This could waste valuable time if those folders are huge.
The following script will not bother scanning the "%basedir%\Projects" or "%basedir%\Archive" folders.
#echo off
setlocal EnableDelayedExpansion
set basedir=C:\folder
>"%basedir%\Projects\list.txt" (
findstr /mil request "%basedir%\*"
for /f "eol=: delims=" %%F in (
'dir /a:d-h /b %basedir% ^| findstr /vixl "projects archive"'
) do findstr /smil request "%basedir%\%%F\*"
)
If you want to skip all folders named "Projects" or "Archive", regardless where they appear in the tree, then:
#echo off
setlocal EnableDelayedExpansion
set basedir=C:\folder
>"%basedir%\Projects\list.txt" (
findstr /mil request "%basedir%\*"
for /f "eol=: delims=" %%F in (
'dir /s /a:d-h /b %basedir% ^| findstr /vir "[\\]projects[\\] [\\]archive[\\] [\\]projects$ [\\]archive$"'
) do findstr /mil request "%%F\*"
)
I had a similar problem: I needed to use findstr to search all .js files except those in the node_modules folder (i.e., I wanted to search my code, but not the code of any imported modules). This is the command I used:
dir /S /B *.js | findstr /v /i node_modules | findstr /i /F:/ todo
Breaking that down:
dir /S /B *.js will output the full path of all .js files in the current directory and all subdirectories
findstr /v /i node_modules filters that list of paths and removes any path that contains the string "node_modules". (The /v flag makes findstr output lines that do not match.)
findstr /i /F:/ todo - The "/F:/" tells findstr to accept a list of file paths to search from the console.
So, only files that make it through the "node_modules" filter get searched.
your problem is: with the redirection > you overwrite your list.txt every time; the last time you overwrite it with an empty string.
Use >> (append to file) instead. The rest of your code is working for me.
Your code have a couple points difficult to follow. You want to skip folders, but for /f %%g in ('dir /a:-h /b %basedir% command get all non-hidden names, including both files and folders. At end you use > to store the results, so just the output of last findstr ... %%g is stored in that file. You must use >> instead as Stephan indicate. However, I still don't understand how you get that result!
I suggest you to modify your code in order to made it simpler, so it may be easier to follow and detect possible errors. For example:
setlocal EnableDelayedExpansion
set basedir=C:\folder
set omitfolders=\Projects\Archive\
cd %basedir%
for /D %%g in (*) do (
if "!omitfolders:\%%g\=!" equ "%omitfolders%" (
findstr /i /m /s /c:"request" %basedir%\%%g\*.* >> %basedir%\Projects\list.txt
)
)
The if "!omitfolders:\%%g\=!" equ "%omitfolders%" command test if the folder name is not in omitfolders variable.

Batch script with for loop and pipe

I would like all the csv files in a directory which filename does not contain word "summary". Inside the command prompt I can type the following command
dir /b my_dir\*.csv | find /V "summary"
When I try to transfer the above command into a batch file I run into a problem in that the pipe command is not supported in the for loop. That is I cannot do the following
FOR /f %%A in ('dir /b my_dir\*.csv | find /V "summary"') do (
rem want to do something here
)
Can somebody shed some light to me on how to solve the problem above?
Thanks in advance!
You need to escape the | character to prevent its being interpreted at the time of parsing the loop command. Use ^ to escape it:
FOR /f "delims=" %%A in ('dir /b "my_dir\*.csv" ^| find /V "summary"') do (
rem do what you want with %%A here
)
Once escaped, the | becomes part of the '-delimited string. It is only interpreted as a special symbol when that string is parsed separately from the loop, as a "sub-command", according to the syntax. And that is done after parsing the loop.
If you get the problem that Gilbeg got "find: /V': No such file or directory" then it's most likely you have cygwin, or similar, in your path and the batch file's not using the Windows find command. If you modify your script to use the absolute path of the Windows find then the error will go away:
FOR /f "delims=" %%A in ('dir /b "my_dir\*.csv" ^| %SYSTEMROOT%\system32\find.exe /V "summary"') do (
rem want to do something here with %%A
)
You can also just embed a double-quoted string inside the single-quotes string, as in:
FOR /f "delims=" %%A in ('"dir /b my_dir\*.csv | find /I /V "summary""') do #(
ECHO Do something with "%%A"
)
have a look at the Windows PowerShell. Not that I have ever used it myself, mind.

Resources