I use a .bat file to move a CSV that is created every day. I now need to clean it up:
find and replace "-" to 0
find and replace empty cells with 0
remove all spaces
The CSV is only 20 lines.
Don't really know, what you need. Please explain more, if this doesn'w work for you:
for /f "delims=" %%a in (old.csv) do (
set "line=%%a"
setlocal enabledelayedexpansion
set "line=!line: =!"
set "line=!line:-=0!"
>> new.csv echo(!line!
endlocal
)
Batch files are not a good tool for this. You'd be far better off using PowerShell:
(Get-Content 'C:\path\to\in.csv') `
-replace ' ', '' `
-replace '-', '0' `
-replace '^,', '0,' `
-replace ',$', ',0' `
| % {
while ($_ -match ',,') { $_ = $_ -replace ',,', ',0,' }
$_
} | Out-File 'C:\path\to\out.csv'
or even VBScript:
Set fso = CreateObject("Scripting.FileSystemObject")
inFilename = "C:\path\to\in.csv"
outFilename = "C:\path\to\out.csv"
Set inFile = fso.OpenTextFile(inFilename)
Set outFile = fso.OpenTextFile(outFilename, 2, True)
outFile.WriteLine inFile.ReadLine 'write header
Do Until inFile.AtEndOfStream
line = inFile.ReadLine
line = Replace(line, " ", "")
line = Replace(line, "-", "0")
If Left(line, 1) = "," Then line = "0" & line
If Right(line, 1) = "," Then line = line & "0"
Do While InStr(line, ",,") > 0
line = Replace(line, ",,", ",0,")
Loop
outFile.WriteLine line
Loop
inFile.Close
outFile.Close
#ECHO OFF
SETLOCAL
(
FOR /f "delims=" %%i IN (yourfile.csv) DO (
SET "line=%%i"&CALL :process
)
)>newcsv.csv
GOTO :EOF
:process
SET line=%line: =%
SET line=%line:-=0%
SET line2=%line%
SET line=%line:,,=,0,%
IF NOT "%line%"=="%line2%" GOTO process
IF "%line:~0,1%"=="," SET line=0%line%
ECHO %line%
GOTO :EOF
Now the interweb fairies are finally back on board, here's my version...
Related
I tried to find a many way to finish this but still no hope ( tried with array etc ...)
Is there a way that I can select part of the file listed in the dir and output it as below
for example if I input 0-2, it output as
00000\.MTS+00001.MTS+00002.MTS
same , if I input 3-5,
00003\.MTS+00004.MTS+00005.MTS
EXAMPLE FOLDER LIST
2022-03-24 00:14 \<DIR\> .
2022-03-24 00:14 \<DIR\> ..
2022-03-23 15:47 5,025,792 00000.MTS
2022-03-23 15:47 4,958,208 00001.MTS
2022-03-23 15:47 3,938,304 00002.MTS
2022-03-23 15:47 9,185,280 00003.MTS
2022-03-23 15:48 9,179,136 00004.MTS
2022-03-23 15:48 3,028,992 00005.MTS
The reason I try to do this it is because I would like to join the MTS files without typing in one by one in the command. The original command is like
copy /b 00000.MTS+00001.MTS+00002.MTS+00003.MTS+00004.MTS+00005.MTS "C:\\Users\\Desktop\\1.MTS"
Hope I can use the batch command
1st : 0-2
copy /b 00000.MTS+00001.MTS+00002.MTS "C:\\Users\\Desktop\\1.MTS"
2nd : 3-5
copy /b 00003.MTS+00004.MTS+00005.MTS "C:\\Users\\Desktop\\2.MTS"
Is it possible to complete this in bat ?
I did try with the for loop to gen the file list,
setlocal enabledelayedexpansion
set /P "mts1=MTS SET 1 : "
for %%n in (%mts1%) do (
echo !FILE_LIST\[%%n\]!+
)
but seem it only show all and also every file uses a new line, cannot be use in copy /b
#ECHO Off
SETLOCAL ENABLEDELAYEDEXPANSION
rem The following settings for the source directory and destination directory are names
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"
SET "destdir=%sourcedir%"
:: Do we have 2 arguments?
IF "%~2" equ "" ECHO need 2 arguments&GOTO :eof
:: calculate the destination filename
SET /a destfile=0
:destloop
SET /a destfile+=1
IF EXIST "%destdir%\%destfile%.MTS" GOTO destloop
:: change to source directory
PUSHD "%sourcedir%"
:: then build the list of files to concatenate
SET "sourcelist="
:again
FOR /f "tokens=1*delims=" %%b IN ('dir /b /a-d /on 0*.MTS ') DO (
SET "candidate="
SET /a candidate=1%%~nb 2>NUL
IF DEFINED candidate (
SET /a serial=candidate -100000
IF !serial! geq %1 IF !serial! leq %2 SET "sourcelist=!sourcelist!+%%~nxb"
IF !SERIAL! GEQ %2 IF "%3" NEQ "" SHIFT&SHIFT&GOTO AGAIN
)
)
IF DEFINED sourcelist COPY %sourcelist:~1% "%destdir%\%destfile%.MTS" >nul
:: back to original directory
POPD
GOTO :EOF
First step: get the destination filename by incrementing a counter and seeing whether that filename already exists.
Next: scan a basic directory list of the .MTS filenames that start 0. For each candidate found, set candidate to empty then attempt to user an arithmetic set (set /a) string 1 in front so that the number becomes, eg 100000. This is because numbers that start 0 are considered as octal by cmd. If the resultant string is not numeric, the set /a will fail, an error message will be produced (the 2>nul suppresses the error message) and candidate will remain set to empty, ie. undefined.
If candidate is defined, then subtract the 100000 that was added in, and check whether the result is greater than or equal to the first parameter AND less than or equal to the second parameter. If so, build sourcelist with the new name preceded by +.
Finally, do the copy using substringing to drop the leading + from sourcelist.
Run the batch with two parameters, the start and end numbers, eg. thisbatch 3 7
Amendment in response to comment:
Insert the label :again and add the extra line IF !serial! geq %2...
When the first range is ended, set serial to the current 3rd argument. If serial is now defined, shift out the first two arguments and start over, with sourcelist already part-built.
Note that this allows any pairs to be used, so thisbatch 12 15 3 6 is valid, as is thisbatch 12 14 3 7 6 11
--- Small revision
#ECHO Off
SETLOCAL ENABLEDELAYEDEXPANSION
rem The following settings for the source directory and destination directory are names
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"
SET "destdir=%sourcedir%"
:: Do we have 2 arguments?
IF "%~2" equ "" ECHO need 2 arguments&GOTO :eof
:restart
:: calculate the destination filename
SET /a destfile=0
:destloop
SET /a destfile+=1
IF EXIST "%destdir%\%destfile%.MTS" GOTO destloop
:: change to source directory
PUSHD "%sourcedir%"
:: then build the list of files to concatenate
SET "sourcelist="
:again
IF /i "%1"=="E" GOTO docopy
FOR /f "tokens=1*delims=" %%b IN ('dir /b /a-d /on 0*.MTS ') DO (
SET "candidate="
SET /a candidate=1%%~nb 2>NUL
IF DEFINED candidate (
SET /a serial=candidate -100000
IF !serial! geq %1 IF !serial! leq %2 SET "sourcelist=!sourcelist!+%%~nxb"
IF !SERIAL! GEQ %2 IF "%3" NEQ "" SHIFT&SHIFT&GOTO AGAIN
)
)
:docopy
IF DEFINED sourcelist COPY %sourcelist:~1% "%destdir%\%destfile%.MTS" >nul
:: back to original directory
POPD
IF /i "%1"=="E" shift&GOTO restart
GOTO :EOF
This gives you the best of both worlds.
If you have a command line thisbatch 3 6 0 2 then it will generate 1 new file with 3+4+5+6+0+1+2.
If you have a command line thisbatch 3 6 0 2 E 12 15 0 2 E 1 4 then it will generate 3 new files with 3+4+5+6+0+1+2, 12+13+14+15+0+1+2 and 1+2+3+4.
The names for each number-pair are concatenated as before. If the next "start number" is E (either case) then the copy command is executed, the E is shifted out and the entire process restarted with arguments of whatever was beyond the E by returning to the label with the bizarre name :restart.
This can be used at a cmd prompt or in a batch-file script. The function builds an array of desired numbers. The desired numbers are expressed as a string similar to many applications specifying page numbers (ie: "1-3" or "3-5,7,10-12")
Place the Do-ConcatFiles.bat and Do-ConcatFiles.ps1 script in the same directory. If you are on a supported windows system, PowerShell is available.
=== Do-ConcatFiles.bat
#ECHO OFF
powershell -NoLogo -NoProfile -File "%~dpDo-ConcatFiles.ps1" -PagesList 1-3,12 -Path ".\pageslist.txt"
=== Do-ConcatFiles.ps1
[CmdletBinding()]
param (
[Parameter(Mandatory=$true)]
[string]$PagesList = '1-3,5,7-12'
,[Parameter(Mandatory=$true)]
[string]$Path = 'C:\src\t\pageslist\pageslist.txt'
)
function Get-PagesList([string]$PagesString) {
# Build an array of desired numbers.
$Pages = #()
$PagesString -replace '\s*' -split ',' |
ForEach-Object {
if ($_ -match '(\d*)\-(\d*)') {
for ([int]$i = [int]$Matches[1]; $i -le [int]$Matches[2]; $i++) { $Pages += $i }
} else { $Pages += $_ }
}
return $Pages
}
<# TEST FILE GENERATION
1..20 | ForEach-Object {
$FileName = ('{0:000000}' -f $_) + '.txt'
Set-Content -Path $FileName -Value $FileName -Encoding ascii
}
#>
Write-Verbose "Input PagesList = $PagesList"
Write-Verbose "Input result file is $Path"
$Pages = Get-PagesList($PagesList)
if ($Pages.Count -ne 0) {
if (Test-Path -Path $Path) { Remove-Item -Path $Path }
$Files = Get-ChildItem -Path '[0-9]*.txt'
$Pages |
ForEach-Object {
foreach ($File in $Files) {
if (($File -match '(\d*).txt') -and ([int]$Matches[1] -eq $_)) {
Write-Verbose "Appending file $File to $Path"
Get-Content -Path $File | Out-File -FilePath $Path -Append -Encoding ascii
}
}
}
}
I have multiple text files contains integer on each row. Can loop for /f calculate (add) from each row of each file sequentially?
Lets say the text files are like this:
File1.txt
123
213
321
File2.txt
111
222
333
File3.txt
333
222
111
Is it possible iterate over multiple files content and sum the like rows IE:
file1 row1 + file2 row1 + file3 row1
file1 row2 + file2 row2 + file3 row2
file1 row3 + file2 row3 + file3 row3
:: operation for 1st row of each file should be:
Set /A calc=123+111+333
echo !calc!
After googling around, I could not find any solution similar to my problem.
Appreciate if anyone can provide insight on this.
Thanks
you can use a single for loop over the output of findstr to build counts from each lines value.
#Echo off & CD /D "%~dp0"
cls
Setlocal
For /f "tokens=1 Delims==" %%g in ('set Line[ 2^> nul')Do Set "%%g=" 2> nul
For /f "tokens=2,3 delims=:" %%i in ('%Systemroot%\System32\Findstr.exe /n /r "^[0123456789]*$" "file*.txt"')Do (
Set /A "Line[%%i]+=%%j+0" 2> nul
)
Set Line[
Endlocal
You can use an array this way:
#echo off
setlocal EnableDelayedExpansion
for %%f in (file*.txt) do (
set "i=1"
for /F %%n in (%%f) do set /A "calc[!i!]+=%%n, i+=1"
)
set /A i-=1
for /L %%i in (1,1,%i%) do echo calc[%%i] = !calc[%%i]!
This batch-file that is run by cmd uses a PowerShell script. It creates a hash item for each line and accumulates the sum for each.
#powershell.exe -NoLogo -NoProfile -Command ^
"$h = #{};" ^
"Get-ChildItem -Filter 'filefile*.txt' |" ^
"ForEach-Object {" ^
"$LineNumber = 0;" ^
"Get-Content -Path $_.FullName |" ^
"ForEach-Object { $h[$LineNumber++] += [int]$_ }" ^
"};" ^
"0..($h.Count-1) | ForEach-Object { $h[$_] }"
This is a lot easier and clearer if written in a .ps1 file.
$h = #{}
Get-ChildItem -Filter 'filefile*.txt' |
ForEach-Object {
$LineNumber = 0
Get-Content -Path $_.FullName |
ForEach-Object { $h[$LineNumber++] += [int]$_ }
}
0..($h.Count-1) | ForEach-Object { $h[$_] }
You may see this get downvoted by someone who thinks PowerShell is not part of cmd. PowerShell is just as much a part of cmd as are find.exe, ipconfig.exe, and setx.exe. PowerShell is available on all supported Windows systems.
You can use findstr in a single for loop and set results sequentially per file:
#echo off & setlocal enabledelayedexpansion
for /f "tokens=1*delims=:" %%i in ('findstr /R "[0-9]" file*.txt') do (
set /a %%~ni+=1 & set /a _Result[!%%~ni!]+=%%j
)
set _Result
Each line per file's lines will be added together, result (based of your current examples:
For a drop single sum files on me approach:-
Summation.bat
#echo off & SETLOCAL ENABLEDELAYEDEXPANSION & Title Summation
for /f "tokens=1* delims=[]" %%A in ('find /n /v "%Title%" "%~1"') do (set "L%%A=%%B" & set "count=%%A")
set calc=0 & for /L %%i in (1,1,!count!) do (set /a calc = !calc! + !L%%i!)
echo/ & echo Line Count = !count! ^& Sum = !calc! & echo/ & pause
Command Line Usage >Summation File1.txt
NOTE as per comment by #T3RROR below
I misread your question as "Provide summation for a file at a time."
HOWEVER have kept it here for others wishing to sum each file.
Good batching, and may the cmd be with you :-)
I want to get the last two line and compare their time, and judge whether the time difference more than 20 seconds. Below is My batch file script and test file. I have successfully got the last two line and print out. When I split the time and compare it, I found the time will leading zero. How could I comare the two time? thanks in advance!
My test file:
08:00:05 I 0-ChecK Loop = 0 Start
08:00:05 I 8124-ChecK Loop = 8124 Start
08:00:15 I 8125-ChecK Loop = 8125 Start
08:00:25 I 8126-ChecK Loop = 8126 Start
08:00:35 I 8127-ChecK Loop = 8127 Start
08:00:42 I 8128-ChecK Loop = 8128 Start
My batch file:
#echo off
setlocal enabledelayedexpansion
for %%A in (C:\Users\Howard\Desktop\test.txt) do (
set "firstline="
set "secondline="
for /f "delims=" %%B in ('type "%%A"') do (
set firstline=!secondline!
set secondline=%%B
)
echo %%A !secondline!
)
for /f "tokens=1,2,3" %%i in ("!firstline!") do (
set time1=%%i
set I1=%%j
set count1=%%k
)
echo %time1%
::set /A T1="%time1:~1,2% * 24"
::set /a T1=(%time1:~0,2% * 24) + (%time1:~3,2% * 60) + (%time1:~6,2% *60)
::echo %T1%
echo %I1%
echo %count1%
for /f "tokens=1,2,3" %%i in ("!secondline!") do (
set time2=%%i
set I2=%%j
set count2=%%k
)
echo %time2%
echo %I2%
echo %count2%
pause
output:
Here's a small extension of my commented example which should simply report whether the timespan was over twenty seconds:
#%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe -NoProfile -Command "$Content = Get-Content -LiteralPath '%UserProfile%\Desktop\test.txt' -Tail 2; If ((New-TimeSpan -Start ($Content[0] -Split ' ')[0] -End ($Content[1] -Split ' ')[0]).TotalSeconds -GT 20) {Exit 2}"
#If ErrorLevel 2 Echo Timespan is over twenty seconds.
I'm trying to go through a .csv file I've copied in a .txt, and find any line that has any of these characters \ / : * ? " ' < >.
In this code I use find to output all line containing "/", but how can I look for every line with one of the special characters above?
#echo off
setlocal
for /f "delims=" %%I in ('powershell -noprofile "iex (${%~f0} | out-string)"') do (
mkdir Output
copy "%%~I" Output\csvCopy.csv
)
for /f "tokens=2,* delims=;" %%a in (Output\csvCopy.csv) do ( echo %%b >> Output\debug.txt )
find /N "/" Output\debug.txt > Output\finalDebug.txt
start "" Output\finalDebug.txt
pause
#REM delete folder output
goto :EOF
: end Batch portion / begin PowerShell hybrid chimera #>
Add-Type -AssemblyName System.Windows.Forms
$f = new-object Windows.Forms.OpenFileDialog
$f.InitialDirectory = pwd
$f.Filter = "CSV Files (*.csv)|*.csv|All Files (*.*)|*.*"
$f.ShowHelp = $true
$f.Multiselect = $false
[void]$f.ShowDialog()
if ($f.Multiselect) { $f.FileNames } else { $f.FileName }
I have searched the vast Internet and cannot find a way to look for specific words in, and only in, the last 10 lines of a text file using batch code. So far, I have this which looks for text in the whole file.
>nul find "Example Words" examplefile.txt && (
REM Found...
goto next) || (
REM Not Found...
goto home
)
So, again, what this does is it searches the whole file for Example Words, while what I need is a way to look for those words in only the last 10 lines?
First, count the number of lines present in the text file:
for /F %%C in ('^< "examplefile.txt" find /C /V ""') do set "COUNT=%%C"
Then determine how many lines to be skipped (all but the last 10 lines, for instance):
if %COUNT% GTR 10 (set /A "SKIP=COUNT-10") else (set "SKIP=0")
Finally, use the more command to actually skip the lines:
more +%SKIP% "examplefile.txt" | > nul findstr /L /I "Example Words" && (
rem Found
goto next
) || (
rem Not Found
goto home
)
Use findstr rather than find to specify multiple SPACE-separated search strings or words (like Example and Words here). Use the /I option to do case-insensitive searches.
Note that more expects user input when the input files contains 65535 or more lines. It converts SPACEs to TABs, but this should not change anything here as you are not interested in them anyway for the search since you are searching for words only.
Here is a more complicated and less efficient approach relying on a for /F loop, using also the COUNT variable as derived above. It needs the skip option of for /F, which is build as follows:
set /A "SKIP=COUNT-10"
if %SKIP% GTR 0 (set "SKIP=skip=%SKIP%") else (set "SKIP=")
Then there is the aforementioned for /F loop:
for /F "%SKIP% delims=" %%L in ("examplefile.txt") do (
echo(%%L| > nul findstr /L /I "Example Words" && (
rem Found
goto next
)
)
ren Not Found
goto home
Every line is searched individually in the loop; as soon as a match is encountered, goto next is executed; goto breaks the loop context, hence for /F does no longer execute any commands inside of its body.
Regard that for /F ignores empty lines, and also such beginning with a semicolon (;) per default. Some special characters (^, &, (, ), ", <, >, |) may cause trouble with the echo command in the pipe (|).
This includes/excludes n lines from top or bottom of a file.
Cut
filter cut {t|b} {i|x} NumOfLines
Cuts the number of lines from the top or bottom of file.
t - top of the file
b - bottom of the file
i - include n lines
x - exclude n lines
Example
cscript //nologo c:\folder\filter.vbs cut t i 5 < "%systemroot%\win.ini"
The script filter.vbs
Set Arg = WScript.Arguments
set WshShell = createObject("Wscript.Shell")
Set Inp = WScript.Stdin
Set Outp = Wscript.Stdout
Set rs = CreateObject("ADODB.Recordset")
With rs
.Fields.Append "LineNumber", 4
.Fields.Append "Txt", 201, 5000
.Open
LineCount = 0
Do Until Inp.AtEndOfStream
LineCount = LineCount + 1
.AddNew
.Fields("LineNumber").value = LineCount
.Fields("Txt").value = Inp.readline
.UpDate
Loop
.Sort = "LineNumber ASC"
If LCase(Arg(1)) = "t" then
If LCase(Arg(2)) = "i" then
.filter = "LineNumber < " & LCase(Arg(3)) + 1
ElseIf LCase(Arg(2)) = "x" then
.filter = "LineNumber > " & LCase(Arg(3))
End If
ElseIf LCase(Arg(1)) = "b" then
If LCase(Arg(2)) = "i" then
.filter = "LineNumber > " & LineCount - LCase(Arg(3))
ElseIf LCase(Arg(2)) = "x" then
.filter = "LineNumber < " & LineCount - LCase(Arg(3)) + 1
End If
End If
Do While not .EOF
Outp.writeline .Fields("Txt").Value
.MoveNext
Loop
End With
Here's a example code counting folders by counting the lines in the dir command output.
#SETLOCAL ENABLEDELAYEDEXPANSION
#CD C:\newfolder
#set count=0
#For /f "delims=" %%A in ('dir C:\newfolder\*.* /ad /s /b') Do (
set /a count=!count!+1
#rem If !Count! gtr 1 echo Num of folders
)
Set /a num=%random% %% %Count% + 1
Echo %num%
#For /f "skip=%num% delims=" %%A in ('dir C:\newfolder\*.* /ad /s /b') Do (
Echo this is nth random folder
Echo Copy "%~dpnx0" "%%A"
Exit /b
)