Batch - Inverse/Negative FOR loops? - loops

I'm trying to concatenate many files into two individual files.
The first file will be a concatenation of all other files with "bob" in the filename. The second file will be a concatenation of all files WITHOUT "bob" in the filename. Both files will output the name of the file before actually doing the concatenation.
Here's what I have so far:
#echo off
setlocal EnableDelayedExpansion
set bob=All_bob.txt
set jimmy=All_jimmy.txt
if exist %bob% del %bob%
if exist %jimmy% del %jimmy%
for %%a in (*bob*.txt) do (
echo /* >>%bob%
echo * %%a >>%bob%
echo */ >>%bob%
copy/b %bob%+"%%a" %bob%
echo. >>%bob%
echo. >>%bob%)
for %%a not in (*bob*.txt) do (
echo /* >>%jimmy%
echo * %%a >>%jimmy%
echo */ >>%jimmy%
copy/b %jimmy%+"%%a" %jimmy%
echo. >>%jimmy%
echo. >>%jimmy%)
However, the second FOR loop (at the bottom) doesn't want to play nice using "not", and using an exclamation point like this...
for %%a !(*bob*.txt) do (
...doesn't want to work, either. So how do I concatenate files that do NOT contain what is inside the parenthesis?

I don't think there is a clean solution to this.
You could probably use FINDSTR to filter %%a but that would require turning *bob*.txt into a regular expression and that is probably not easy to automate.
Another (ugly) solution is to use nested loops:
echo bob:
for %%a in (*bob*.txt) do (
echo %%a
)
echo not bob:
for %%a in (*) do (
setlocal ENABLEDELAYEDEXPANSION&set inc=1
for %%b in (*bob*.txt) do if "%%~a"=="%%~b" set inc=0
if "!inc!"=="1" echo %%a
endlocal
)

How about using find:
for /F %%a in ('dir /b *.txt') do (
echo %%a | find /V "bob")
This should return all .txt files that don't have "bob" in them.

Using findstr and a regular expression for *bob*.txt:
for /f "usebackq delims=" %%a in (`dir /b ^| findstr ".*bob.*\.txt"`) do (…)
Just use the /V switch to process all other files:
for /f "usebackq delims=" %%a in (`dir /b ^| findstr /v ".*bob.*\.txt"`) do (…)
You can use the help command or the /? switch for for or findstr for more information.
I cleared the delimiters (delims=) to allow for spaces in the filenames.

Related

findstr "^" doesn't work but "^^" does to lookup the beginning lines

My findstr /c:"^string" works in funny way.
To lookup top of each lines using findstr /r with "^", is there any way to put the same query? At least, I found where there's variable as file name, /c:"^^abc" picks up properly, but /c:"^abc" doesn't.
>type test.txt
abc
abc
>type test.bat
setlocal enabledelayedexpansion
set Myfile=test.txt
for /F "usebackq delims=" %%i in (`findstr /n /r /i /c:"^abc" "!Myfile!"`) do (
echo %%i
)
goto :eof
>test.bat
1: abc
2:abc
>
Only when I put 2 "^"s, i.e /c:"^^abc", then,
...
for /F "usebackq delims=" %%i in (`findstr /n /r /i /c:"^^abc" "!Myfile!"`) do (
...
>test.bat
2:abc
>
Or, when I put %Myfile% instead of !Myfile!, it looks working with c:"^abc". But, Myfile will vary through each run. So %variable%, or immediate_value won't work for me.
Is there any ways to put a Universal lookup (any switch available?) so that I don't need to check each time I write codes? This would reduce quite a lot of time to debug.
FYI, Replacing !Myfile! with %%x by putting one more "For" loop works with Single "^". This is the only way I can forget about "^^"against !Myfile! for now.
>type test2.bat
setlocal enabledelayedexpansion
set Myfile=test.txt
for /F "usebackq delims=" %%h in (`dir /a-d /b "!Myfile!"`) do (
for /F "usebackq delims=" %%i in (`findstr /n /r /i /c:"^abc" "%%h"`) do (
echo %%i
)
)
goto :eof
>test2.bat
2:abc
>

Batch script to delete files in folder that does not contain certain word

I am new to batch scripting . I need to delete all files in a folder that DOES NOT contains some word in the file
found this code
#echo off
setlocal
pushd C:\Users\admin\Desktop\bat
findstr /ip /c:"importantWord" *.txt > results.txt
popd
endlocal
So how i can WHITE list this files, and delete all other?
Or i think there is easy way with just check if !contains and delete
but i don`t know how?
Supposedly, this problem could be solved in a very simple way combining these findstr switches: /V that show results when the search string is not found, and /M that show just the name of the files; that is:
#echo off
setlocal
cd C:\Users\admin\Desktop\bat
for /F "delims=" %%a in ('findstr /ipvm /c:"importantWord" *.txt') do del "%%a"
Unfortunately, the combination of /V and /M switches don't properly work: the result of /V is based on lines (not files), so a modification in the method is needed:
#echo off
setlocal
cd C:\Users\admin\Desktop\bat
rem Create an array with all files
for %%a in (*.txt) do set "file[%%a]=1"
rem Remove files to preserve from the array
for /F "delims=" %%a in ('findstr /ipm /c:"importantWord" *.txt') do set "file[%%a]="
rem Delete remaining files
for /F "tokens=2 delims=[]" %%a in ('set file[') do del "%%a"
This method is efficient, particularly with big files, because findstr command report just the name of the files and stop searching after the first string match.
#echo off
setlocal
set "targetdir=C:\Users\admin\Desktop\bat"
pushd %targetdir%
for /f "delims=" %%a in ('dir /b /a-d *.txt') do (
findstr /i /p /v /c:"importantWord" "%%a" >nul
if not errorlevel 1 echo del "%%a"
)
popd
endlocal
Not really sure what you want to do with /pfiles - files containing non-ansi characters appear to return errorlevel 1for these. if not errorlevel 1 will echo the files that do not contain the required string - remove the echo to actually delete the file(s)
This should work:
#ECHO OFF
SETLOCAL EnableDelayedExpansion
SET "pathToFolder=C:\FolderToEmpty"
SET "wordToSearch=ImportantWord"
FOR /F "tokens=*" %%F IN ('dir %pathToFolder% /b *.txt') DO (
findstr /IP %wordToSearch% "%pathToFolder%\%%F">nul
IF !ERRORLEVEL!==1 (
DEL /Q "%pathToFolder%\%%F"
)
)
You will have to set the proper path to the folder you want to delete the files from and to replace ImportantWord with the substring you are looking for.

Delete duplicate top-level folder names from list with batch

I need to delete duplicate folder names from a folder list. The duplicates occur when there's more than 1 subfolder. I end up with a list like below. I want to get rid of any line that has a sub2 folder.
folder1\sub1
folder2\sub1
folder2\sub1\sub2
folder3\sub1
Following code works if there is only one sub2 foldername, but it's awkward--hopeless if more than one sub2. There's gotta be a better way. Any help much appreciated.
#Echo off
SETLOCAL EnableDelayedExpansion
:: Write the sub2 foldernames to a tmp file
For /f "tokens=3 delims=\" %%I IN (folderlist.txt) DO Echo %%I >>temp.tmp
:: Set var for each sub2 name in tmp file and
:: call routine to write lines that don't contain that name
For /f %%G in (temp.tmp) do (
Set findstring=%%G
CALL :FindDup
)
EXIT
:findDup
For /f %%H in ('Type folderlist.txt ^|Find "!findstring!" /v') Do (
Echo %%H >> NoDup.txt
)
exit /b
FWIW: I'm using this command to generate the list, then deleting the path preceding folder1, folder2, etc
For /d %%G in (*) do dir /ad /on /s /b "%%G" >> folderlist.txt
you are almost there, if you just want the resulting list after eliminating the subfolders, just try to echo the appropiate lines to the list file, having copied it first into a temporary.
move folderlist.txt %temp%\folders.txt
for /f "tokens=1,2,* delims=\" %%a in (%temp%\folders.txt) do (
if .%%c==. echo %%a\%%b >>folderlist.txt
)
if you want to remove the folder from the disk, then change the line to
if not .%%c==. rd /s %%a\%%b\%%c
#ECHO OFF
SETLOCAL
FOR /f "delims=" %%a IN (q20840158.txt) DO (
FOR /f "tokens=1,3delims=\" %%b IN ("%%a") DO IF "%%c"=="" ECHO(%%a
)
GOTO :EOF
I used a file named q20840158.txt containing your data for my testing.
But - It's really unclear what you mean by a "duplicate". How precisely do you define a duplicate in this context?
#ECHO OFF
SETLOCAL
COPY NUL newfile.txt >NUL 2>nul
FOR /f "delims=" %%a IN (q20840158.txt) DO (
ECHO %%a|FINDSTR /I /L /g:"newfile.txt" >NUL 2>NUL
IF ERRORLEVEL 1 >>"newfile.txt" ECHO(%%a
)
TYPE newfile.txt
GOTO :EOF
I used a file named q20840158.txt containing your data for my testing.
Produces newfile.txt
Ah- you want to make sure that any additions to the list don't contain a previous entry...

Batch FOR /F loop won't read relative directory

I have a simply FOR /F loop which strips out all but one line of a text file:
for /f "skip=12 tokens=* delims= " %%f in (.\NonProcessed\*.txt) do (
> newfile.txt echo.%%f
goto :eof
)
But when I run, I get the result:
The system cannot find the file .\NonProcessed\*.txt
The for loop works fine if I enter a fully qualified path to the text file within the brackets, but it can't handle the relative link I have in there. I've been able to use the exact same relative link in another standard for loop in a different batch file running in the same directory without any issues. I can't understand why it won't work! Please help.
EDIT: For comments, code I'm using now is
for %%f in (.\NonProcessed\*.txt) do (
echo f is %%f
for /f "usebackq skip=12 tokens=* delims= " %%a in (%%f) do (
echo a is %%a
> %%f echo.%%a
goto :continue
)
:continue
sqlcmd stuff here
)
Sorry but for /f does not allow you to do that. And no, the problem is not the relative path to files but the wildcard.
According to documentation, you have the syntax case
for /F ["ParsingKeywords"] {%% | %}variable in (filenameset) do command [CommandLineOptions]
For this case, documentation states The Set argument specifies one or more file names. You can do
for /f %%a in (file1.txt file2.txt file3.txt) do ...
but wildcards are not allowed.
If you don't know the name of the file you want to process, your best option is to add an additional for command to first select the file
for %%a in (".\NonProcessed\*.txt"
) do for /f "usebackq skip=12 tokens=* delims= " %%f in ("%%~fa"
) do (
> newfile.txt echo(%%f
goto :eof
)
When executed, the goto command will cancel both for loops so you end with the same behaviour you expected from your original code.
edited to adapt code to comments
#echo off
set "folder=.\NonProcessed"
pushd "%folder%"
for /f "tokens=1,2,* delims=:" %%a in (
' findstr /n "^" *.txt ^| findstr /r /b /c:"[^:]*:13:" '
) do (
echo Overwrite file "%%a" with content "%%c"
>"%%a" echo(%%c
)
popd
Read all the files in the folder, numbering the lines. The output for the first findstr command will be
filename.txt:99:lineContents
This output is parsed to find the line 13, the resulting data is splitted using the colon as a separator, so we will end with the file name in %%a, the line number in %%b and the line content in %%c.
SET FILES_LIST=files_list.config
DIR /b .\NonProcessed\*.txt>!FILES_LIST!
for /f "skip=12 tokens=* delims= " %%f in (!FILES_LIST!) do (
> newfile.txt echo.%%f
goto :eof
)
IF EXIST "!FILES_LIST!" DEL "!FILES_LIST!"
I did not check how your's FOR works, just added my additions/corrections to it.... Hope it will work for you.
Best regards!

Querying a registry key in a batch script

I'm using the following code to get a list of programs being run at start up, and log them to a file.
for /f "skip=2 tokens=1,2*" %%A in ('REG QUERY "HKCU\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Run" 2^>NUL') do echo %%A : %%C >> Log.txt
This works with entries where the value name doesn't contain a space, but when it does, such as with "Google Update", it messes up the tokens, and %%C becomes: REG_SZ <path>, instead of just the path.
Does anyone have a better way to query the registry and log its values?
Well I got one working solution, I'd still love to see if anyone has anything better.
for /f "skip=2 tokens=*" %%A in ('REG QUERY "HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Run" 2^>NUL') do (
set regstr=%%A
set regstr=!regstr: =^|!
for /f "tokens=1,3 delims= |" %%X in ("!regstr!") do (
echo %%X : %%Y
)
)
Version specific, works in XP, does not work in Win 7 - see comments for details.
Columns in the output are separated by tab char (0x09), so use only tab as a separator:
for /f "skip=2 tokens=1,2* delims= " %%A
This does not show well because of how markup handles white chars, but the character after delims= must be actual TAB
Here's a better way via WMI calling the Win32_StartupCommand class, results output to screen as well as a CSV file in the same folder as per script name:
#echo off
setlocal enabledelayedexpansion
cd \ & pushd "%~dp0"
if exist "%~n0.tmp" (del /f /q "%~n0.tmp")
if exist "%~n0.csv" (del /f /q "%~n0.csv")
wmic /namespace:\\root\cimv2 path Win32_StartupCommand get /all /format:csv >"%~n0.tmp"
for /f "tokens=1,2,3,4,5,6,7,8,9 usebackq delims=, skip=2" %%a in (`type "%~n0.tmp"`) do (
echo %%b, %%c >>"%~n0.csv"
echo %%b, %%c
)
if exist "%~n0.tmp" (del /f /q "%~n0.tmp")
popd & endlocal
exit /b 0`

Resources