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
}
}
}
}
Related
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 need to do a batch file which copys a .txt file and edits the new file.
In the new file the first two lines should be deleted.
After that there needs to be a line break after every 355th character.
So the source .txt looks something like this:
useless line 1
useless line 2
single line with 1516539 characters....
And the new .txt file should look like this:
Line 1 with 355 characters
Line 2 with 355 characters
Line 3 with 355 characters
Line 4 with 355 characters
.
.
.
This is my .bat so far.
#echo off
copy Y:\Dat_6\Log6.txt C:\Users\<user>\Desktop
pause
As already mentioned, I would suggest powershell for this task. To keep this on topic, you can run it directly from your batch-file:
#"%__AppDir__%WindowsPowerShell\v1.0\powershell.exe" -NoProfile ^
"Get-Content \"Y:\Dat_6\Log6.txt\" | ForEach-Object {" ^
"$line = $_; for ($i = 0; $i -lt $line.Length; $i += 355) {" ^
"$length = [Math]::Min(355, $line.Length - $i); $line.SubString($i, $length)" ^
"} } | Select-Object -Skip 2 |" ^
"Set-Content \"P:\ath To\something.ext\" -Encoding ASCII"
Here you'd modify output file path on line 6, and if necessary your input filename on line 2. If you do that, please note that each path should be enclosed in escaped doublequotes, \"
The following batch script reads the long line of your text file by set /P, which reads it in portions of about 1K characters/bytes, does that in a loop, and returns portions of text in a nested loop:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_FILE=Y:\Dat_6\Log6.txt"
set /A "_NUM=2" & rem // (number of lines to skip)
set /A "_LEN=355" & rem // (number of characters per line)
rem // Do file processing in sub-routine:
< "%_FILE%" call :PROCESS
endlocal
exit /B
:PROCESS
setlocal EnableDelayedExpansion
set "PREV="
rem // Skip first few (short) lines:
for /L %%I in (1,1,%_NUM%) do set /P LINE=""
:PROCESS_GET
rem // Read more text:
set "LINE=" & set /P LINE=""
rem // Stop if no (more) text is available:
if not defined LINE (
if defined PREV echo(!PREV!
endlocal & exit /B
)
rem // Prepend previous unhandled text:
set "LINE=!PREV!!LINE!"
:PROCESS_CUT
rem // Check whether more text is needed:
if "!LINE:~%_LEN%!"=="" set "PREV=!LINE!" & goto :PROCESS_GET
rem // Return a text portion:
echo(!LINE:~,%_LEN%!
rem // Split off remaining text:
set "LINE=!LINE:~%_LEN%!"
rem // Process remaining text:
goto :PROCESS_CUT
endlocal
exit /B
Note that this script is completely untested (yet)…
I am trying to list down all the Files and its parameters within folder recursively to list all the files in the directories/sub directories that has size greater than 10000 bytes and not modified more than 30 days. The problem is that the below code just gets stuck giving no output.
#echo on
SET Path1=Z:\Server_P8BE890914E369FBB6FBD0C91748F8B19
break > Z:\FilestobeDeleted.txt
ECHO FILE RAN %date:~10%/%date:~4,2%/%date:~7,2% >>%CDID%\FilestobeDeleted.txt
echo FileName Size Path Date Time >>Z:\FilestobeDeleted.txt
for /f "skip=1 tokens=1,2,3,4,5* delims=,] " %%i in ('forfiles /p %Path1% /s /m *.* /c "cmd /c if #fsize gtr 10000 echo #fsize #file #path #fdate #ftime" /d -30') do (
set FILE_SIZE_WINDOWS=%%i
set FILE_NAME=%%j
set FILE_PATH=%%k
set FILE_DATE=%%l
set FILE_TIME=%%m
set "unit=B"
for %%b in (KB MB GB TB PB EB ZB YB) do if 1024 lss !FILE_SIZE_WINDOWS! (
set "unit=%%b"
if !FILE_SIZE_WINDOWS! lss 2147483647 (set /a "FILE_SIZE_WINDOWS=FILE_SIZE_WINDOWS/1024") else (set "FILE_SIZE_WINDOWS=!FILE_SIZE_WINDOWS:~0,-3!")
)
echo !FILE_NAME! !FILE_SIZE_WINDOWS!!unit! !FILE_PATH! !FILE_DATE! !FILE_TIME! >>Z:\FilestobeDeleted.txt
)
echo Done
Here's an example powershell solution which tries to list, in Z:\FilestobeDeleted.txt, the file name, size, fully qualified path, and modified date and time of all files in your source directory and its subdirectories which are greater than or equal to 1MB and which were last modified over 30 days ago. It uses a switch statement to assign the appropriate unit to those sizes and rounds each to a maximum of two decimal places.
Function Convert-BytesToHighest {
[CmdletBinding()]
Param([Parameter(Mandatory=$False,Position=0)][Int64]$Size)
Switch ($Size) {
{$Size -GT 1PB} {$NewSize="$([Math]::Round(($Size/1PB),2))PB";Break}
{$Size -GT 1TB} {$NewSize="$([Math]::Round(($Size/1TB),2))TB";Break}
{$Size -GT 1GB} {$NewSize="$([Math]::Round(($Size/1GB),2))GB";Break}
{$Size -GT 1MB} {$NewSize="$([Math]::Round(($Size/1MB),2))MB";Break}
{$Size -GT 1KB} {$NewSize="$([Math]::Round(($Size/1KB),2))KB";Break}
Default {$NewSize = "$([Math]::Round($Size,2))Bytes";Break}
}
Return $NewSize
}
Get-ChildItem -Path "Z:\Server_P8BE890914E369FBB6FBD0C91748F8B19" -Recurse -Force -ErrorAction SilentlyContinue |
Where-Object {
(! $_.PSIsContainer) -And ($_.Length -GE 1048576) -And ($_.LastwriteTime -LT (Get-Date).AddDays(-30))
} |
Format-Table Name,#{Expression={Convert-BytesToHighest $_.Length};Label="Size"},FullName,LastWriteTime -AutoSize |
Out-File -FilePath "Z:\FilestobeDeleted.txt" -Width 512
You should be able to run it from a batch-file or cmd like this:
PowerShell -ExecutionPolicy RemoteSigned -File "MyScript.ps1"
If you find that some information gets truncated due to very long file paths, you can increase the Width value as necessary.
file 1
A
B
C
file 2
B
C
D
file1 + file2 =
A
B
C
D
Is it possible to do using cmd.exe?
If you can affort to use a case insensitive comparison, and if you know that none of the lines are longer than 511 bytes (127 for XP), then you can use the following:
#echo off
copy file1.txt merge.txt >nul
findstr /lvxig:file1.txt file2.txt >>merge.txt
type merge.txt
For an explanation of the restrictions, see What are the undocumented features and limitations of the Windows FINDSTR command?.
Using PowerShell:
Get-Content file?.txt | Sort-Object | Get-Unique > result.txt
For cmd.exe:
#echo off
type nul > temp.txt
type nul > result.txt,
copy file1.txt+file2.txt temp.txt
for /f "delims=" %%I in (temp.txt) do findstr /X /C:"%%I" result.txt >NUL ||(echo;%%I)>>result.txt
del temp.txt
First part (merging two text files) is possible. (See Documentation of copy command)
copy file1.txt+file2.txt file1and2.txt
For part 2, you can use sort and uniq utilities from CoreUtils for Windows. This are windows port of the linux utilities.
sort file1and2.txt filesorted.txt
uniq filesorted.txt fileunique.txt
This has a limitation that you will lose track of original sequencing.
Update 1
Windows also ships with a native SORT.EXE.
Update 2
Here is a very simple UNIQ in CMD script
You may also use the same approach of Unix or PowerShell with pure Batch, developing a simple uniq.bat filter program:
#echo off
setlocal EnableDelayedExpansion
set "prevLine="
for /F "delims=" %%a in ('findstr "^"') do (
if "%%a" neq "!prevLine!" (
echo %%a
set "prevLine=%%a"
)
)
EDIT: The program below is a Batch-JScript hybrid version of uniq program, more reliable and faster; copy this program in a file called uniq.bat:
#if (#CodeSection == #Batch) #then
#CScript //nologo //E:JScript "%~F0" & goto :EOF
#end
var line, prevLine = "";
while ( ! WScript.Stdin.AtEndOfStream ) {
line = WScript.Stdin.ReadLine();
if ( line != prevLine ) {
WScript.Stdout.WriteLine(line);
prevLine = line;
}
}
This way, you may use this solution:
(type file1.txt & type file2.txt) | sort | uniq > result.txt
However, in this case the result lost the original order.
The solution below assume that both input files are sorted in ascending order using the same order of IF command's comparison operators and that does not contain empty lines.
#echo off
setlocal EnableDelayedExpansion
set "lastLine=ÿ"
for /L %%i in (1,1,10) do set "lastLine=!lastLine!!lastLine!"
< file1.txt (
for /F "delims=" %%a in (file2.txt) do (
set "line2=%%a"
if not defined line1 set /P line1=
if "!line1!" lss "!line2!" call :advanceLine1
if "!line1!" equ "!line2!" (
echo !line1!
set "line1="
) else (
echo !line2!
)
)
)
if "!line1!" neq "%lastLine%" echo !line1!
goto :EOF
:advanceLine1
echo !line1!
set "line1="
set /P line1=
if not defined line1 set "line1=%lastLine%"
if "!line1!" lss "!line2!" goto advanceLine1
exit /B
this joins, sorts and reduce excessive size after PowerShell
Get-Content file?.txt | Sort-Object | Get-Unique | Set-Content -Encoding UTF8 result.txt