Hey code gurus, I wasn't sure where to post this so mods please advise and move it to the appropriate forum if need be. I have a repetitive task that I want to automate via a batch script in Windows or DOS: Drive or directory has a bunch of SUBFOLDERS: EG D:\folder 1, D:\folder 2, etc... it could be further nested but we can cd to the host drive or folder. In EACH SUBFOLDER, there is a text file using the same exact name as in other folders: EG D:\folder 1\blah.txt, D:\folder 2\blah.txt Each of those files have unique content - EG they are descriptions of the content of each folder but use the same name per folder. I want to make a copy of the blah.txt file in each folder into another file while keeping the original file in each folder. I DO NOT WANT TO copy a master blah.txt file into each subfolder!!! Thus I want to parse the root drive or folder, list out each subfolder, find the target file, make a copy with a new name, then go to the NEXT folder and do the same operation until all folders have been been checked and done. Note: Some of those subfolders already have both blah.txt and blahx.txt. So if the "to be copied" file name exists, I want the script to check and skip it. If a folder DOES NOT even have the original blah.txt, then it should also skip the copy operation in that folder, and goto the next folder and repeat the operation until the last folder has been checked. I only want the script to detect 1 level deep from my source directory so that if there are subsequent nested folders in the target folder, it would not go any further. I don't care if the subfolders are sorted by date, ascending, descening or whatever order (I believe default is ascending by folder name) so long as the source and target files are the same - keeping the original date and time stamps so I know it is a copy. If any code gurus can advise, or advise a program - I tried to use Total Commander to do a seek and list out the source file, then to make a copy into a but it seems to want to make copies into ONE folder, not into each original folder. Edit --- I may need a second script.. to parse the blah.txt text in each folder and check that the name variable has "" surrounding the text otherwise, the file is useless: EG: blah.txt contents: name = "best mod ever"; <--this is the correct format name = best mod ever; <--this is the incorrect format, would not show up in a launcher, and should be "fixed" to the above This can be processed via a second or pre batch file instead of integrated into the copy batch file. If anyone is curious why I am requesting this, I am trying to fix and fool a broken game launcher to detect mods for ARMA 3. Seems the coder who wrote the launcher is gone and no word from Bohemia on how to fix it! Appreciate any assistance...
@mbk1969 - awesome if you can advise how to do this. You get my eternal thanks and a virtual Christmas card! I did try to find some online tutorials but it was only bits and pieces of the operations I wanted and appear to be almost as much work to do it manually... for thousands of folders... ugh...
@Valken If you place a space between "@" and user name notification to that user is not fired. So what do you choose - CMD or PowerShell? I prefer PowerShell since it has much easier and clean syntax for folder enumeration tasks. But you can choose on your own preferences. PS What is actual name of a master file - "blah.txt" or "blat.txt"?
First PowerShell script to copy the files: Code: <# .SYNOPSIS Performs the search for "blah.txt" files in specified folder (and 1 level subfolders) and copies them to "blahx.txt" files. .DESCRIPTION The Copy-Files.ps1 script searches the specified folder and 1 level subfolders for "blah.txt" files and copies them to "blahx.txt" files. .PARAMETER Folder Specifies the path to the folder. Parameter has default value of "C:\Game" - this value is used when script is executed without specifying the value for parameter. You can change this value for proper folder path. .PARAMETER File Specifies the name for the source files (to search for). Parameter has default value of "blah.txt". .PARAMETER CopyTo Specifies the name for the copy of the source file. Parameter has default value of "blahx.txt" .INPUTS None. You cannot pipe objects to Copy-Files.ps1. .OUTPUTS Copy-Files.ps1 reports all visited folders. .EXAMPLE C:\PS> .\Copy-Files.ps1 .EXAMPLE C:\PS> .\Copy-Files.ps1 -folder C:\Game .EXAMPLE C:\PS> .\Copy-Files.ps1 -folder C:\Game -file "blah_h.txt" .EXAMPLE C:\PS> .\Copy-Files.ps1 -folder C:\Game -file "blah_h.txt" -copyto "blah_hx.txt" #> #Requires -Version 3 param ([string]$Folder = "C:\Game", [string]$File = "blah.txt", [string]$CopyTo = "blahx.txt") $ErrorActionPreference = "Stop" function CopyFile { param([string]$path) # check whether the source file exists $sourcePath = Join-Path $path $File if(!(Test-Path $sourcePath -PathType Leaf)) { "Folder '{0}' has no master file" -f $path return } # check whether the target file exists $targetPath = Join-Path $path $CopyTo if(Test-Path $targetPath -PathType Leaf) { "Folder '{0}' already has the copy of master file" -f $path return } "Folder '{0}': copying '{1}' to '{2}'..." -f $path, $File, $CopyTo Copy-Item -Path $sourcePath -Destination $targetPath } # check the existence of specified folder path if(!(Test-Path $Folder -PathType Container)) { throw "Specified folder doesn't exist" } # copy file in specified root folder CopyFile($Folder) # enumerate subfolders foreach($sub in (Get-ChildItem $Folder -Directory)) { CopyFile($sub.FullName) } Pause Copy and paste the code and save it to file "Copy-Files.ps1". The script file can be executed either from context menu of Explorer, or from already started PowerShell session. Script reports each enumerated folder, and if you execute it in PowerShell session you can redirect whole report to some text file. Note the line Code: param ([string]$Folder = "C:\Game", [string]$File = "blah.txt", [string]$CopyTo = "blahx.txt") - there you should edit the default path for folder, the name of master file and the name of copy file. The script requires PowerShell v3 (or later). PowerShell v2 was shipped only with Win7. So if you are on Win7 then I suggest for you to install PowerShell v3 or newer. If you have never executed PowerShell scripts you should set the policy regarding such execution. Launch PowerShell and execute Code: Set-ExecutionPolicy RemoteSigned
@mbk1969 - just returned and thank you! d/l'ing PS v3 now and will see how it goes!!! Edit -- It WORKS!!! Here is a sample output log (I just copied it from the PS3 console): Folder 'F:\Arma 3\107410\1445226945' has no master file Folder 'F:\Arma 3\107410\1446397227' has no master file Folder 'F:\Arma 3\107410\1446466038' already has the copy of master file Folder 'F:\Arma 3\107410\1446500688' already has the copy of master file Folder 'F:\Arma 3\107410\1446740501' already has the copy of master file Folder 'F:\Arma 3\107410\1447351824' already has the copy of master file Folder 'F:\Arma 3\107410\1447559322' already has the copy of master file But I did check the directory sub folders - host directory was F:\Arma 3\107410\ and it did copy and created the target file. The original file name was meta.cpp and I set the script to create mod.cpp so the game launcher can detect it. I used blah originally because I forgot what the file name was at the time of writing. Also, thanks for looking into the name declaration correction / fixer. In both meta.cpp and mod.cpp it should be: name = "xxx"; where xxx is any name string with or without spaces. Many of the description files (meta.cpp or mod.cpp - text files really) have errors like: name = xxx; without the "". Sometimes the format may be: name="xxx"; which is also correct for some reason so long as the "" are there. Note there are other descriptors inside the meta.cpp and mod.cpp such as below for a properly setup mod folder off steam workshop: meta.cpp: protocol = 1; publishedid = 0; name = "VSM Backup"; timestamp = 5248411705097409551; mod.cpp: name = "VSM - Project Zenith"; picture = "VSM_DLC.paa"; description = "VSM - Project Zenith"; logo = "VSM_DLC.paa"; logoOver = "VSM_DLC.paa"; tooltip = "VanSchmoozinMods"; author = "VanSchmoozin"; overviewPicture = "VSM_DLC.paa"; The mod maker is supposed to create both *.cpp files but in absence of the mod.cpp file, we can use the meta.cpp copied file to substitute the name argument. The rest is nice to have. But the most important line is the NAME which would not show up under the launcher without the " " after the = sign and before ; . I don't need you to fix format all the other lines.. Many thanks for looking into this! I already have another batch script cleanup idea but it involves creating batch symbolic links to REPLACE duplicate files while keeping game file directories intact. I will make a separate post about it as I am unsure of the DOS commands to do it.
@Valken Here is the second script Code: <# .SYNOPSIS Performs the search for cpecified files in specified folder (and 1 level subfolders) and correct the content. .DESCRIPTION The Correct-Files.ps1 script searches for specified files in specified folder (and 1 level subfolders) and correct the line 'name = XXX;' by enclosing the 'XXX' part in double quotation marks. .PARAMETER Folder Specifies the path to the folder. Parameter has default value of "F:\Arma 3\107410\1445226945" - this value is used when script is executed without specifying the value for parameter. You can change this value for proper folder path. .PARAMETER File Specifies the name for the source files (to search for). Parameter has default value of "meta.cpp". .INPUTS None. You cannot pipe objects to Copy-Files.ps1. .OUTPUTS Correct-Files.ps1 reports all incorrect files and files without the 'name=XXX;' pattern. .EXAMPLE C:\PS> .\Correct-Files.ps1 .EXAMPLE C:\PS> .\Correct-Files.ps1 -folder C:\Game .EXAMPLE C:\PS> .\Correct-Files.ps1 -folder C:\Game -file "mode.cpp" #> #Requires -Version 3 param ([string]$Folder = "F:\Arma 3\107410\1445226945", [string]$File = "meta.cpp") $ErrorActionPreference = "Stop" function CheckAndCorrectFile { param([string]$path) # check whether the source file exists $sourcePath = Join-Path $path $File if(!(Test-Path $sourcePath -PathType Leaf)) { "Folder '{0}' has no master file" -f $path return } [string]$content = Get-Content $sourcePath -ReadCount 0 # check whether pattern 'name=value;' present at all if(!($content -imatch 'name\s*=[^;]*;')) { "File '{0}' has no pattern 'name=value;' at all" -f $sourcePath return } # check for valid pattern if($content -imatch 'name\s*=\s*"[^"]*"\s*;') { return } "File '{0}': correcting content..." -f $sourcePath # check for missing opening '"' if($content -imatch 'name\s*=\s*[^"]*"\s*;') { $newContent = @(Get-Content $sourcePath | ForEach-Object { $_ -ireplace '(name\s*=\s*)([^"]*)("\s*;)', '$1"$2$3' }) $newContent | Set-Content $sourcePath } # check for missing closing '"' elseif($content -imatch 'name\s*=\s*"[^"]*\s*;') { $newContent = @(Get-Content $sourcePath | ForEach-Object { $_ -ireplace '(name\s*=\s*")([^"]*)(\s*;)', '$1$2"$3' }) $newContent | Set-Content $sourcePath } else { $newContent = @(Get-Content $sourcePath | ForEach-Object { $_ -ireplace '(name\s*=\s*)([^;]*)(\s*;)', '$1"$2"$3' }) $newContent | Set-Content $sourcePath } } # check the existence of specified folder path if(!(Test-Path $Folder -PathType Container)) { throw "Specified folder doesn't exist" } # check file in specified root folder CheckAndCorrectFile($Folder) # enumerate subfolders foreach($sub in (Get-ChildItem $Folder -Directory)) { CheckAndCorrectFile($sub.FullName) } Pause - I called it Correct-Files.ps1. I put your values for folder and file in block Code: param ([string]$Folder = "F:\Arma 3\107410\1445226945", [string]$File = "meta.cpp") - but you can put any values there. I extended the testing code to catch not only pattern 'name=XXX;' but also patterns 'name="XXX;' and 'name=XXX";'. And if file has no pattern 'name=;' at all it will be reported with that info. PS If you need other patterns to correct just say. Also the script can be transformed to check not the 'name=XXX;' pattern but any 'YYY=XXX;' one.
@mbk1969 - tested and it worked great.. see the log output for your reference: Spoiler File 'F:\Arma 3\107410\1182344748\mod.cpp': correcting content... Folder 'F:\Arma 3\107410\1182706279' has no master file Folder 'F:\Arma 3\107410\1183509581' has no master file Folder 'F:\Arma 3\107410\1183662508' has no master file Folder 'F:\Arma 3\107410\1183822272' has no master file Folder 'F:\Arma 3\107410\1184116319' has no master file Folder 'F:\Arma 3\107410\1184209589' has no master file Folder 'F:\Arma 3\107410\1185376633' has no master file Folder 'F:\Arma 3\107410\1186259231' has no master file Folder 'F:\Arma 3\107410\1186610523' has no master file Folder 'F:\Arma 3\107410\1186862802' has no master file Folder 'F:\Arma 3\107410\1187541696' has no master file Folder 'F:\Arma 3\107410\1187994422' has no master file Folder 'F:\Arma 3\107410\1188198738' has no master file Folder 'F:\Arma 3\107410\1188644414' has no master file Folder 'F:\Arma 3\107410\1188684560' has no master file Folder 'F:\Arma 3\107410\1188693650' has no master file Folder 'F:\Arma 3\107410\1188738690' has no master file Folder 'F:\Arma 3\107410\1188935625' has no master file File 'F:\Arma 3\107410\1189320435\mod.cpp' has no pattern 'name=value Folder 'F:\Arma 3\107410\1190115445' has no master file Folder 'F:\Arma 3\107410\1190141639' has no master file Folder 'F:\Arma 3\107410\1190547137' has no master file Folder 'F:\Arma 3\107410\1192855511' has no master file Folder 'F:\Arma 3\107410\1193212167' has no master file Folder 'F:\Arma 3\107410\1193472784' has no master file Folder 'F:\Arma 3\107410\1193506664' has no master file Folder 'F:\Arma 3\107410\1193542134' has no master file Folder 'F:\Arma 3\107410\1194872203' has no master file File 'F:\Arma 3\107410\1195440524\mod.cpp': correcting content... I did this to both -file mod.cpp and -file meta.cpp. I will load up the launcher and see if it detects the corrections properly and advise back again!