Delete string in multiple files in directory with cmd - batch-file

:-)
I'm very new to cmd-commands and .bat-stuff, so would be grateful if any of you could help me with a problem.
So...I have a folder with 116 .txt files.
All these .txt files contain lines starting with a "#" and some other lines starting with "---".
I figured that findstr /v /L /C:"#" test.txt > test2.txt works in creating a new file without any lines that contain "#"
Now my question, is it possible to write something like findstr /v /L /C:"#" & "---" *.txt > *.txt ?
The goal is that each existing file is overwritten and all lines containing either "#" or "---" are removed.
Glad for any help!
Cheers,
Dyz

#ECHO OFF
SETLOCAL
rem The following setting for the source directoryis a name
rem that I use for testing and deliberately include names which include spaces to make sure
rem that the process works using such names. These will need to be changed to suit your situation.
SET "sourcedir=u:\your files\t w o"
FOR /f "delims=" %%w IN ('dir /b /a-d "%sourcedir%\*.txt"') DO FINDSTR /v /L /c:"#" /c:"---" "%sourcedir%\%%w">"%sourcedir%\tempfile"&MOVE /y "%sourcedir%\tempfile" "%sourcedir%\%%w" >nul
GOTO :EOF
The meat of the matter is the for/f line, obviously.
Run dir /b to obtain a list of the filenames. /a-d excludes directorynames. This list is built in memory first, as the .txt files are being replaced - this ensures that the files are only "listed" once.
The for /f "delims=" reads that list and assigns each line-contents (filename+extension only) to %%w. The delims= ensures that filenames containing spaces are properly processed.
Then execute the findstr, which can take multiple /c: arguments to or the strings.
[I haven't tried it, but FINDSTR /v /L "# ---" "%sourcedir%\%%w" should produce identical processing]
The output of the findstr is directed to a temporary filename (I chose to put it in the source directory, but it could be anywhere with any name) and then the temporary file is moved over the original with the 1 file moved message suppressed by a >nul.
As always, test first on a copied sample.

Related

How to remove digits from beginning of filename?

I need to rename a filename like this 7612372 filename 50x50.jpg into this filename 50x50.jpg, removing all digits at the beginning of the filename.
The number of digit can be variable.
I need to integrate this into an existing batch file run from the Windows command prompt.
If the format of the filename would be the same for all the files in the folder, then you can try:
#echo off
for /F "delims=" %%A IN ('dir /b /A-D') do (
for /F "tokens=2-3" %%B IN ("%%A") do (
ren "%%~fA" "%%B %%C"
)
)
This is the shortest way, but not the most accurate one. It is unsecure, because if the filename contains spaces, the file will be rename incorrectly. I suggest the following code for the task:
#echo off
setlocal EnableDelayedExpansion
for /F "delims=" %%A IN ('dir /b /A-D') do (
set filename=%%A
for /F "tokens=1" %%B IN ("%%A") do (
ren "%%~fA" "!filename:%%B =!"
)
)
which is more accurate and renames all files correctly only if they have the format mentioned in the beginning.
#echo off turns command-echoing off.
setlocal EnableDelayedExpansion enables delayed expansion. We use it only here, as we have to access variables inside a for loop which is a code block. You must use delayed expansion always inside these code blocks.
Now we make a for loop to parse the output (/F) of the dir /b /A-D command which lists all items in current working directory (%cd%), excluding directories (/A-D).
We need to set a variable here with the filename. We could use the variable name of the loop (%%A), but variables have an advantage: %var:search=replace%, or even !var:search=replace! which we need here.
Now we make another for loop to parse a string (/F): the filename (%%A). We need to access the first token to substract it later. We don't really need to specify it here, but it is good to make it clearer.
We rename files now: %%~fA is the full path where filename currently processed is and !filename:%%B =! means to take filename environment variable, search for string "%%B " (first part of filename [digits] and a space) and replace it with an empty string; actually nothing!
An easier solution is to use
all digits and space as delims and
tokens=*
:: Q:\Test\2019\01\06\SO_54054587.cmd
for /F "delims=" %%A in (
'dir "* *" /A-D-H /B 2^>nul'
) do for /F "tokens=* delims=0123456789 " %%B in (
"%%A"
) do ren "%%A" "%%B"
this will remove all leading delimiters while not splitting the remainder of the file name.
Like the other answers this will not account for the shorted file name already being present.
Your question is not specific enough for us to provide a solution, you really need to provide the section of code into which you wish this to be integrated.
This one expects only one file, as in your question, and that file must be named in the format you've indicated, i.e. the required part is separated from the non-required part by a space:
#Set "name=7612372 filename 50x50.jpg"
#Ren "%name%" "%name:* =%"
[Edit /]
I have noted from your comments that you were indeed looking to parse several files and those files did not match the naming scheme you provided in your question.
Here therefore is an updated potential solution based on those changed parameters.
#For %%A In (*.*) Do #For /F "Tokens=* Delims=0123456789 " %%B In ("%%A") Do #Ren "%%~A" "%%B"
Apologies to LotPings, who I've noticed has posted a very similar method/solution
It's very simple with the basic DOS command rename.
7612372 filename 50x50.jpg
If this is your sample file in the folder, it contains 7 digits and 1 blank space. Totally 8 characters.
We can do this by simply running this command on the particular folder
rename "*.mp3" "////////*.mp3"
each / represents a character you want to remove. That's it.
I suggest following batch code for this task:
#echo off
for /F "delims=" %%A in ('dir "* *" /A-D-H /B 2^>nul') do for /F "tokens=1*" %%B in ("%%A") do ren "%%A" "%%C"
pause
The command FOR runs with cmd.exe /C (more precise %ComSpec% /C) in a separate command process in background the command line:
dir "* *" /A-D-H /B 2>nul
DIR outputs to handle STDOUT of this background command process
just the names of all non-hidden files because of option /A-D-H (attribute not directory and not hidden)
in bare format because of option /B without file path
matching the wildcard pattern * * which matches any file name with at least one space inside
in current directory which can but must not be the directory of the batch file.
DIR would output an error message to handle STDERR if it can't find any directory entry matching these criteria. This error message is redirected to device NUL to suppress it.
Read the Microsoft article about Using Command Redirection Operators for an explanation of 2>nul. The redirection operator > must be escaped with caret character ^ on FOR command line to be interpreted as literal character when Windows command interpreter processes this command line before executing command FOR which executes the embedded dir command line with using a separate command process started in background.
FOR captures all lines output to handle STDOUT of started command process and processes those lines after started cmd.exe terminated itself. It is very important for this file renaming task that FOR runs on a list of file names captured before doing the file renames as otherwise the directory entries would change while FOR is accessing them. For that reason for can't be used directly in this case because of for would process the list of * * directory entries while this list changes on each successful file rename. The result would be files not renamed or renamed multiple times or even an endless running loop depending on file system (NTFS or a FAT file system like FAT32 or ExFAT).
FOR with option /F ignores empty lines which do not occur here. FOR ignores also lines starting with a semicolon because of end of line option eol=; is the default. But all lines output by DIR should start with a number and for that reason the default end of line definition can be kept for this task.
FOR with option /F splits up a line by default to substrings using normal space and horizontal tab as delimiters and assigns just first space/tab separated string to specified loop variable. This line splitting behavior is not wanted here in outer FOR loop because loop variable A should hold complete file name with all spaces. Therefore delims= is used to define an empty list of delimiters to disable the line splitting behavior. Safer would be "delims= eol=" which defines also no end of line character.
The file name assigned to loop variable A is referenced with %%A as string in inner FOR loop which splits up the file name into two substrings (tokens). The first substring is the number assigned to specified loop variable B. The second substring after first sequence of spaces (tabs not possible in a file name) is assigned without any further splitting to next loop variable C according to ASCII table. In other words on file name 7612372 filename 50x50.jpg loop variable B holds 7612372 and filename 50x50.jpg is assigned to loop variable C.
The command REN renames the file by referencing complete file name as assigned to loop variable A to the part after first sequence of spaces as assigned to loop variable C.
The command PAUSE at end is added to see the error message output by command REN if renaming a file failed. There is nothing output except the prompt by PAUSE on all files could be renamed successfully.
The batch code can be enhanced further by using FINDSTR as filter to make sure that a file to rename starts really with one or more digits up to first space by using this code:
#echo off
for /F "delims=" %%A in ('dir "* *" /A-D-H /B 2^>nul ^| %SystemRoot%\System32\findstr.exe /R /C:"^[0123456789][0123456789]* "') do for /F "tokens=1*" %%B in ("%%A") do ren "%%A" "%%C"
pause
One more variant for renaming a file with name 03T30 NAME T ALL 40X40X2 - Copy.JPG to T30 NAME T ALL 40X40X2 - Copy.JPG:
#echo off
for /F "delims=" %%A in ('dir /A-D-H /B 2^>nul ^| %SystemRoot%\System32\findstr.exe /R "^[0123456789][0123456789]*"') do for /F "tokens=* delims=0123456789 " %%B in ("%%A") do ren "%%A" "%%B"
pause
DIR outputs the names of all non-hidden files in current directory. This output is redirected as input for FINDSTR which checks if the file name starts with one or more digits. Only those file names are output to STDOUT of background command process to be processed next by FOR.
The inner FOR interprets all digits and space character as string delimiters because of delims=0123456789  and assigns everything after first sequence of digits or spaces to loop variable B because of tokens=*. So loop variable B holds filename 50x50.jpg with 7612372 filename 50x50.jpg assigned to A and T30 NAME T ALL 40X40X2 - Copy.JPG for file name 03T30 NAME T ALL 40X40X2 - Copy.JPG.
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.
dir /?
echo /?
findstr /?
for /?
pause /?
ren /?
PS: I recommend the usage of the shareware file manager Total Commander which has a built-in multi-rename tool for renaming files and folders for people with no coding experience. Download, install and start Total Commander, navigate to the folder containing all these files, press Ctrl+A to select the files, press Ctrl+M to open multi-rename tool window and the rest is self-explaining. If you need nevertheless help, press key F1 to open the help page for multi-rename tool.

Batch Create Folders and Relocate Files Based on Partial Filename

I need to create folders and relocate files to the folders based on the first eight characters of the filenames. The files will look like:
GG001652 - 5211_Infoscitex.xls
GG001652 - 5211- as of 7.31.12.pdf
GG001570 - 7575 FSR (3.31.2010).pdf
GG001570 - 7575_IC_6.30.12.xlsx
GG001570 - 7575_SF 425_6.30.12.xls
I'd like the batch to create two folders:
GG001622
GG001570
Each containing the files beginning with those 8 characters. File length and nomenclature is inconsistent, but the first 8 characters are standardized, and file types include .pdf, .doc, .docx, .xls, .xlsx and .msg
Spent a good few hours trying to modify Magoo's code in this post, but couldn't for the life of me get it to work:
Batch create folders based on part of file name and move files into that folder
Have a work deadline I need to meet, so greatly appreciate any help offered.
If the first 8 chars are always followed by space,
use a for /f to split the name with default delimiter space and token 1. See ss64.com/nt/for_f
#Echo off
for /f "delims=" %%A in (
'DIr /B /A-D "* - *.*" ^| findstr /I "^[PG]G[0-9][0-9][0-9][0-9][0-9][0-9].-..* '
) Do for /f %%B in ("%%A") do (
MD %%B >NUL 2>&1
Move "%%A" %%B
)
The first iterating for is now replaced with a more complex for /f parsing dir output wihich is filtered by a findstr with a RegEx to match the described name structure.
a P or G followed by a G followed by 6 numbers.

Batch script to remove "duplicate" files

I have a list of files named:
file.txt
file (1).txt
file (2).txt
etc.
Where the greater (number) is the last file updated.
I want a .bat script that allows get the content of file (maxnumer).txt to file.txt.
dir /B /OD /TW file*.txt lists file names in sort order of last written time (cf. dir /?).
Next commented batch script could do the job for you:
#echo OFF
SETLOCAL EnableExtensions
rem delete empty `_lastfile` variable
set "_lastfile="
rem store file name of last written time into `_lastfile` variable
for /F "delims=" %%G in ('dir /B /OD /TW file*.txt 2^>NUL') do set "_lastfile=%%~G"
rem ↑↑↑↑↑↑ suppress errors
rem check the `_lastfile` variable
rem is defined?
rem AND is different from "file.txt"?
if defined _lastfile if /I "%_lastfile%" NEQ "file.txt" copy /Y "%_lastfile%" "file.txt"
Resources (required reading):
(command reference) An A-Z Index of the Windows CMD command line
(additional particularities) Windows CMD Shell Command Line Syntax
(%~G etc. special page) Command Line arguments (Parameters)
(2>NUL etc. special page) Redirection
Here is a bit of a hacky answer.
This script will move all files from file (1).txt up to file (10).txt to file.txt , leaving only file.txt which now contains the text that was in file (10).txt
Doing it in ascending order will ensure the highest number that exists is the last one to be moved.
#echo off
set /P name=Enter name of file without extension:
echo enter extension:
set /P ext=.
echo. & echo.
echo these actions will be performed: & echo.
FOR /L %%A IN (1,1,10) DO #echo move /y "%name% (%%A).%ext%" "%name%.%ext%"
echo. & pause & echo.
FOR /L %%A IN (1,1,10) DO move /y "%name% (%%A).%ext%" "%name%.%ext%"
pause
You could use IF EXIST %name% (%%A).%ext% To stop the script from trying to move files that don't exist, but it doesn't really affect anything to do that, so I didn't bother.
The script above will do it for a specific named file. To do it for all files in a directory will be possible, here are some hints to get you going
use a dir /b >filenames.txt to get all files in a directory listed in a text file
to perform an action for every line in a textfile do
for /f "usebackq delims= tokens=* %%a in (`filenames.txt`)" DO (
::some stuff here
)
The way I would go about it would be to get filenames.txt , manually delete all the (n) files so you just have a list of the "non-duplicate" filenames, and use that as your input. You
There are probably more elegant solutions out there, but with all the peculiarities of batch I wouldn't be surprised to find they are pages and pages long.
If you want to keep all the original files not just end up with the final file.txt with no copies, then I you want to use COPY
If you want to keep all the original files, then you would want to use COPY not MOVE.
In that case, to remove all superfluous operations (i.e. only copy the highest numbered file, not copy all the files in order) then something like IF NOT EXIST %name% (!B!).%ext% where !B!=%%A+1 within your (now multiline) for loop and use Setlocal EnableDelayedExpansion on to make the arithmetic work properly. But it's not really necessary, copying 1, then 2, then 3, then 4 does the same thing if a little slower than skipping 1 2 and 3 and just doing 4.
I hope this helps enough to point you in the right direction, feel free to ask questions if this isn't clear.

Write list of files with relative path, to file, via batch script

I want to list all files (with relative path) in a folder with a specific sub-folder, and write that list to a text file. The folder is a network folder so I can not set it as current directory. All this via batch script.
So like this:
Folder structure:
\\OtherComputer\MyFiles\libs
--File1.dll
--File2.dll
\\OtherComputer\MyFiles\libs\Editor\
----File3.dll
I want to generate a file with the following text:
File1.dll
File2.dll
Editor\File3.dll
This is how far I have come:
SET LibPath=\\OtherComputer\MyFiles\
break >"%LibPath%metadata"
for %%F in (%LibPath%*.*) do (
echo %%F>>"%LibPath%metadata"
)
for %%F in (%LibPath%Editor\*.*) do (
echo Editor\%%F>>"%LibPath%metadata"
)
But this solution write the full path. I tried this to remove the path:
set str=%%F:%LibPath%=%
But it does not seem to handle variable SET or the %%F variable too well inside a for-loop.
Is it even possible? Would like to not have to write a C# executable for this.
Thanks in advance!
Try the following:
#echo off
setlocal enabledelayedexpansion
set "dir=\\OtherComputer\MyFiles\"
FOR /F "tokens=*" %%f IN ('dir /s /b /A-d "%dir%"') do (
set "full=%%f"
echo !full:*%dir%=!
)
setlocal enabledelayedexpansion, in addition to creating a localized environment, turns on delayed expansions of variables, which allows variables to be modified inside the for loop's body and used with their modified values. (see help set). Note that in order for a variable to be expanded dynamically, you must enclose it in !, not %; e.g., !full! instead of %full%.
Note: A side effect of enabling delayed expansion is that any ! characters are interpreted as part of a variable reference, resulting in potentially unwanted removal of ! chars. from strings, such as in echo hi!. To output a literal ! while delayed expansion is enabled, use ^^! in unquoted strings, and ^! in double-quoted strings. Furthermore, literal ^ chars. in double-quoted strings then have to be represented as ^^.
dir /s /b /A-d "%dir%" lists all files - and files only, due to excluding directories with /A-d - in the current subtree (/s), as paths only (b). Note that using /s implies that full paths are output.
set "full=%%f" sets aux. variable full to the absolute path being processed in the current loop iteration.
!full:*%dir%=! then strips the input dir.'s path from the absolute path using prefix string substitution, leaving the relative path desired (again, see help set).
Just to contrast this with a PowerShell (v3+) solution, which demonstrates how much more advanced PowerShell is compared to cmd.exe.:
$dir = '\\OtherComputer\MyFiles\'
(Get-ChildItem -File -Recurse $dir).FullName.replace($dir, '')
I have a way to to this with two bat files:
code1.bat: Looping through all the files in "C:\Mypath"
#echo off
FOR /F "tokens=*" %%G IN ('dir /s /b C:\Mypath\*.*') do (
CALL code2.bat %%G
)
code2.bat: removing the main path string
SET _var=%1
SET _endbit=%_var:*Mypath\=%
Echo %_endbit%
Probably can be done in one file... take a look at http://ss64.com/nt/
The xcopy commmand is capable of returning relative paths. If the /L switch is given, nothing is actually copied but all items are listed that would be copied without /L. The pushd command handles UNC paths correctly (type pushd /? in command prompt for details). So the following code snippet should do what you want:
#echo off
pushd "\\host\share\folder"
> "\path\to\listfile.txt" xcopy /L /Y /C /I /E ".\*.*" "%TMP%"
popd
The above code results in each line to be prefixed with .\. In addition, it returns a summary line like # File(s). The code below gets rid of all those artefacts:
#echo off
pushd "\\host\share\folder"
> "\path\to\listfile.txt" (
for /F "tokens=2 delims=:" %%I in ('
xcopy /L /Y /C /I /E "*.*" "%TMP%"
') do (
echo(%%I
)
)
popd
Here I changed the file pattern from .\*.* to *.* which results in outputs like Z:rel\path\to\item.ext (rather than .\rel\path\to\item.ext). The for /F loop parses the output of xcopy and removes everything up to the first :, so the drive letter Z: is deleted. Since the : is a forbidden character for file and directory names, it cannot appear in any of the paths. Since the summary line # File(s) does not contain any columns, it is not enumerated by for /F.

Windows Command batch script to add filename to end of each row in a merged text file

I a new to windows command line scripts.
I have a batch file which i use to merge multiple text files into one. However i want to be able to also add the name of the text file the row comes from to the end of each row in the merged file.
This is the script i am currently working with:
#ECHO OFF
ECHO Creating %1...
FOR /F "usebackq tokens=1" %%G IN (`DIR /B "C:\My Documents\Data\*.txt"`) DO
(
ECHO Adding %%G
ECHO. >> Output.txt
TYPE %%G >> Output.txt
)
Now i know that to get the filename into the output file i need to use:
ECHO %%G >> Output.txt
However i'm not sure how i would add this to the current script so it adds the filename to each row and I have had no luck with finding any examples.
There is a simple two liner that works from the command line if you are willing to prefix each line with the file name instead of putting the file name at the end. This solution is extremely fast.
cd "C:\My Documents\Data"
findstr "^" *.txt >output.log
Each line will have the format of filename:line content
It is important that your output file use a different extension than the files you are merging. Otherwise you run the risk of the command processing its own output!
The other option is to make sure the output goes to a different folder, perhaps the parent folder:
cd "C:\My Documents\Data"
findstr "^" *.txt >..\output.txt
Or, if you are willing to include the full path to each file in your output, then make sure current directory is not the same as your source, and use
findstr "^" "C:\My Documents\Data\*.txt" >output.txt
The only drawback to the above solution is that problems can arise if the last line of a text file does not end with a newline character. This will cause the first line of the next file to be appended to the last line of the prior file. Something like: FILENAME1:last line without newlineFILENAME2:first line of next file
A simple script can append a newline to files that are missing the newline on the last line. Then the files can be safely merged with the filename prefix on each line:
#echo off
if "%~1" equ ":FindFiles" goto :FindFiles
cd "C:\My Documents\Data"
:: Append newline to text files that are missing newline on last line
for /f "eol=: delims=" %%F in ('"%~f0" :FindFiles') do echo(>>"%%F"
:: Merge the text files and prefix each line with file name
findstr "^" *.txt >output.log
exit /b
:FindFiles
setlocal enableDelayedExpansion
:: Define LF to contain a newline character
set lf=^
:: The above 2 blank lines are critical - do not remove
:: List files that are missing newline on last line
findstr /vm "!lf!" *.txt
You'll need to add each line in the file individually:
#ECHO OFF
ECHO Creating %1...
SET "sourcedir=c:\sourcedir"
FOR /F "delims=" %%G IN ('DIR /B /a-d "%sourcedir%\*.txt"') DO (
ECHO Adding %%G
ECHO. >> Output.txt
for /f "usebackq tokens=*" %%a in ("%sourcedir%\%%~G") do (
Echo %%a - %%G >> Output.txt
)
)
Note in the second last line, the file name and the line is seperated by a -, you can replace this with whatever (don't forget to check for escaping characters) or can get rid of this if you want.
I'm sure that will work, but if it doesn't, tell me the Error message and I can fix it for you.
Mona
---- [edit:pw]
Close - major problem was the ( on the FOR ... %%G line was on the line following the DO - must be on the same line as the DO.
Added /a-d to the DIR to prevent subdirectory names matching
changed "usebackq tokens=1" to use conventional quotes and allow spaces in filenames
assigned target directory name to sourcedir variable and included %sourcedir% in both FOR statements to allow execution from anywhere, otherwise the filenames found in C:\My Doc.... would be searched-for in the current directory for replication into the output.
OP needs to change value assigned to sourcedir to C:\My
Documents\Data

Resources