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.
Related
I need a batch file that counts certain file types in sub directories. In this case it's .txt and .xls files. At the end of the file it reports this back.
#echo off
REM get script directory
set scriptdir=%~dp0
REM change directory to script directory
cd /d %scriptdir%
setlocal
set txtcount=0
set xlscount=0
for %%x in ('dir *.txt /s ) do set /a txtcount+=1
for %%x in ('dir *.xls /s ) do set /a xlscount+=1
echo %txtcount% text files
echo %xlscount% .xls files
endlocal
pause
My batch file doesn't report back the correct count of files. I thought it might be continually counting but I've set the count variables to local so I'm not sure what's up.
There's three problems with your script:
You want to iterate over command output, so you need FOR /F
You're not using the "bare" /B format, i.e. DIR outputs more than just filenames
You're missing the closing single quote twice
Replace your loops with this:
FOR /F %%X IN ('DIR /S /B *.txt') DO SET /A "txtcount+=1"
FOR /F %%X IN ('DIR /S /B *.xls') DO SET /A "xlscount+=1"
You are very close, but you are missing the switch /B of the dir command, so the output contains a header and a footer, which are both included in the count. Therefore, change dir *.txt /s to dir *.txt /s /b (same for *.xls) and the count is going to be correct.
Besides that, there are typos: the closing ' is missing -- for %%x in ('dir *.txt /s') do .... In addition, the /F switch at for is missing, which is needed to capture the output of a command. (I assume these things are present in the true script as you would receive errors otherwise.)
Alternative Approach
To count the number of files, you could also do the following:
for /F "delims=" %%X in ('dir /B /S "*.txt" ^| find /C /V ""') do set "COUNT=%%X"
find /V "" searches for non-empty lines, hence all lines returned by dir /B /S are considered as matches; the /C switch simply lets find return the total amount of matching lines.
I am attempting to find multiple strings in files in a directory, there are thousands. I currently run the following command to search the directory:
findstr /s "customerid" *
Now this allows me to find the file that contains that string. I normally have two pieces of information a customer id and an event type. One customer can have up to 30 associated event such as "website registration".
What I would like to do is, search the directory for both the customer id and the event. Then copy the file to a new location. Is this possible in a batch file?
Supposing you want to find all files that contain both words (customer and event in this example), you could use the following script:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "PATTERN=*.txt"
set "SOURCE=."
set "TARGET=D:\Data"
set "STRING1=customer"
set "STRING2=event"
pushd "%SOURCE%" && (
for /F "delims=" %%F in ('findstr /S /M /I /R /C:"\<%STRING1%\>" "%PATTERN%"') do (
for /F "delims=" %%E in ('findstr /M /I /R /C:"\<%STRING2%\>" "%%F"') do (
ECHO copy "%%E" "%TARGET%\%%~nxE"
)
)
popd
)
endlocal
exit /B
After having tested the script, remove the upper-case ECHO in front of the copy command!
#echo off
setlocal enableextensions disabledelayedexpansion
set "sourceFolder=x:\somewhere"
set "targetFolder=y:\another\place"
set "customerID=00000000"
set "event=eeeeeeeeee"
for /f "delims=" %%a in ('
findstr /m /s /l /c:"%customerID%" "%sourceFolder%\*"
^| findstr /f:/ /m /l /c:"%event%"
') do (
ECHO copy "%%~fa" "%targetFolder%"
)
findstr can deal with it. We only need two instances
The first one will search all the input files for the first string, returning only the list of matching files. This list of files will be piped into the second instance
The second one will search the second string but only in the files found by the first instance, reading the list of files where to search from standard input stream (/f:/)
The rest of the code is just a for /f wrapping the two findstr commands to process the output of the second one and do the file copy.
After having tested the script, remove the upper-case ECHO in front of the copy command!
I have to move a lot of xml-Files, named as follow:
F010199004524001_904.XML
F010199805946001_737.XML
F010199904725001_611.XML
F030390114543001_901.XML
F030390114544001_257.XML
F030390114545001_901.XML
in my batch-file, there's this line:
move C:\source\F01*.xml C:\target\F01\
Now I have the problem, that some of the files have a different 8.3-filename, for example
"F030390114545001_901.XML" has the name "F01FCF~1.XML" so this file is also moved in the directory "F01".
The command "fsutil 8dot3name strip" in my case is useless, because the batch is running on a 2003 server.
Is there any type of workaround or alternative commands to solve this problem?
for /f %%a in ('dir /b /a:-d "c:\source"^| findstr /i /b "F01"^|findstr /i /e ".xml"') do (
move "%%~fa" "C:\target\F01\"
)
this?
EDIT
findstr filtering can be done with a single expression -
findstr /beri "f01.*xml"
so
for /f %%a in ('dir /b /a:-d "c:\source"^| findstr /beri "f01.*xml"') do (
move "%%~fa" "C:\target\F01\"
)
I have a series of files that have names like this:
CHART_LOAN_6516_20130502.PDF
CHART_LOAN_2158_20130502.PDF
CHART_LOAN_78986_20130502.PDF
Each file always starts with CHART_LOAN_ but the next number is different and the last number is the date it was created.
I would like to insert an 0_ after CHART_LOAN_number_ for each file. As listed below:
CHART_LOAN_6516_0_20130502.PDF
CHART_LOAN_2158_0_20130502.PDF
CHART_LOAN_78986_0_20130502.PDF
Through my research I've found out to insert the char but not when the name is changing with each file.
#echo off
SETLOCAL ENABLEDELAYEDEXPANSION
SET old=CHART_LOAN_
SET new=CHART_LOAN_0_
for /f "tokens=*" %%f in ('dir /b *.jpg') do (
SET newname=%%f
SET newname=!newname:%old%=%new%!
move "%%f" "!newname!"
)
The above code will change the static portion of the file to something I want, but I don't know how to modify the code to compensate for the changing loan number.
try this:
#echo off &setlocal
for /f "tokens=1-3*delims=_" %%i in ('dir /b /a-d *.pdf ^| find /i /v "_0_"') do ren "%%i_%%j_%%k_%%l" "%%i_%%j_%%k_0_%%l"
for /f "tokens=*" %%f in ('dir /b /a-d %old%*.pdf^|find /i /v "%new%"') do (
should see you right – but I'd add an ECHO temporarily before the MOVE to check it's working properly.
Interesting that you'd use *.jpg in your original though. Rather unlikely to work, I'd guess.
The renamed files will match the pattern ..._0_... but the originals won't – the closest they'll come is with CHART_LOAN_0.pdf. Hence, you find all the filenames that don't match the new mask (/v) case-insensitive (/i). The /a-d guards against the remote possibility of a directory name that matches the old mask. The caret before the pipe tells CMD that the pipe is part of the command to be executed by the for.
I have almost no experience in batch, but now I need a script to delete all files that contain certain characters in their names from a folder and its subfolders on Windows 64. I only did simple things in batch like
del "C:\TEST\TEST2\*.TXT"
But I have no idea how to perform the filtering I need.
Could someone help me to achieve this using batch?
EDIT more exactly, the question is "how to tell batch to include subfolders?"
The /s switch exists on quite a few commands (including del)
del /s "C:\TEST\TEST2\*.TXT"
The help for del /? says:
/S Delete specified files from all subdirectories.
Try this:
#echo off & setlocal
set "MySearchString=X"
for /f "delims=" %%i in ('dir /b /s /a-d ^| findstr /i "%MySearchString%"') do echo del "%%~i"
Set the variable MySearchString to the character or string to search and remove the echo command, if the output is OK.
You can also specify the MySearchString for the file name only:
#echo off & setlocal
set "MySearchString=T"
for /r %%a in (*) do for /f "delims=" %%i in ('echo("%%~na" ^| findstr /i "%MySearchString%"') do echo del "%%~fa"