1. This site uses cookies. By continuing to use this site, you are agreeing to our use of cookies. Learn More.

Help with dos/windows batch script

Discussion in 'Programming/Html' started by Valken, Dec 15, 2018.

  1. Valken

    Valken Maha Guru

    Messages:
    1,414
    Likes Received:
    50
    GPU:
    Forsa 1060 3GB Temp GPU
    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...
     
    Last edited: Dec 15, 2018
  2. mbk1969

    mbk1969 Ancient Guru

    Messages:
    6,188
    Likes Received:
    2,351
    GPU:
    GeForce GTX 1070
    I can write such script. Also I can offer PowerShell script instead of CMD one.
     
    Valken likes this.
  3. Valken

    Valken Maha Guru

    Messages:
    1,414
    Likes Received:
    50
    GPU:
    Forsa 1060 3GB Temp GPU
    @mbk1969 - awesome if you can advise how to do this. You get my eternal thanks and a virtual Christmas card! :D

    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...
     
    Last edited: Dec 15, 2018
  4. mbk1969

    mbk1969 Ancient Guru

    Messages:
    6,188
    Likes Received:
    2,351
    GPU:
    GeForce GTX 1070
    @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"?
     
    Last edited: Dec 15, 2018

  5. mbk1969

    mbk1969 Ancient Guru

    Messages:
    6,188
    Likes Received:
    2,351
    GPU:
    GeForce GTX 1070
    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
     
    Last edited: Dec 15, 2018
    Valken and BetA like this.
  6. Valken

    Valken Maha Guru

    Messages:
    1,414
    Likes Received:
    50
    GPU:
    Forsa 1060 3GB Temp GPU
    @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.
     
    Last edited: Dec 15, 2018
  7. mbk1969

    mbk1969 Ancient Guru

    Messages:
    6,188
    Likes Received:
    2,351
    GPU:
    GeForce GTX 1070
    @Valken
    Meanwhile I create correcting script for 'name = best mod ever;' problem.
     
  8. mbk1969

    mbk1969 Ancient Guru

    Messages:
    6,188
    Likes Received:
    2,351
    GPU:
    GeForce GTX 1070
    @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.
     
    Last edited: Dec 15, 2018
    Valken likes this.
  9. Valken

    Valken Maha Guru

    Messages:
    1,414
    Likes Received:
    50
    GPU:
    Forsa 1060 3GB Temp GPU
    @mbk1969 - tested and it worked great.. see the log output for your reference:

    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!
     

Share This Page