0

I am currently writing a powershell script. Its supposed to look through all files in a directory and move them to different directories based on their content. Now, in some of those files are the file name of an associated bitmap for example. How could i make it so that if it sees an associated file, it gets moved to the same folder?

This is my script so far, it distributes the files to 2 different directories based on if they contain the words given in $pattern1 and $pattern2.

$source = 'Z:\Documents\16_Med._App\Aufträge\RuheEKG_24HBP_Skript\Ursprung_test'
$destination = 'Z:\Documents\16_Med._App\Aufträge\RuheEKG_24HBP_Skript\24BHD'
$toDelete = 'Z:\Documents\16_Med._App\Aufträge\RuheEKG_24HBP_Skript\ToDelete'
$pattern1 = '24BHD'
$pattern2 = 'RuheEKG'

$hans = Get-ChildItem $source  

foreach($item in $hans) {
    if (Select-String -list -pattern $pattern1 -path $item.fullname) {
        Move-Item -Path $item.fullname -Destination $destination}
        Else {
        Move-Item -Path $item.fullname -Destination $toDelete}


}

At the end of line 31, you see the associated Bitmap file.

At the end of line 31, you see the associated Bitmap file. I need the script to recognize this as a different file, find it and move it to the same directory as the one the file containing it is in.

All help would be appreciated!

1
  • Could you please edit your question and put in the (relevant part) of an input file as formatted TEXT. Commented Jan 17, 2019 at 21:04

3 Answers 3

1

My guess is that after moving the files and associated bitmap files, you would also want to have the file contain the new filepaths for these referenced files.

Also, since your patterns aren't really regular expressions, I used the -SimpleMatch parameter on Select-String.

This code should do just that.

$source      = 'Z:\Documents\16_Med._App\Aufträge\RuheEKG_24HBP_Skript\Ursprung_test'
$destination = 'Z:\Documents\16_Med._App\Aufträge\RuheEKG_24HBP_Skript\24BHD'
$toDelete    = 'Z:\Documents\16_Med._App\Aufträge\RuheEKG_24HBP_Skript\ToDelete'
$pattern1    = '24BHD'
$pattern2    = 'RuheEKG'

# create the destination paths if they do not exist
if (!(Test-Path -Path $destination -PathType Container)) {
    Write-Host "Creating folder '$destination'"
    New-Item -Path $destination -ItemType 'Directory' -Force | Out-Null
}
if (!(Test-Path -Path $toDelete -PathType Container)) {
    Write-Host "Creating folder '$toDelete'"
    New-Item -Path $toDelete -ItemType 'Directory' -Force | Out-Null
}

# get an array of full path and filenames.
# if they all have the same extension, it would be wise to add the '-Filter' parameter..
$allFiles = @(Get-ChildItem $source -File | Select-Object -ExpandProperty FullName)

foreach($file in $allFiles) {
    # get the file content as array so we can reuse it and update the line(s) with the new bitmap path(s)
    $content = Get-Content -Path $file

    # first decide on the destination. '-Quit' returns $true or $false
    # if both $pattern1 AND $pattern2 are present, move stuff to $destination
    if (($content | Select-String -Pattern $pattern1 -SimpleMatch -Quiet) -and 
        ($content | Select-String -Pattern $pattern2 -SimpleMatch -Quiet)) {
        $dest = $destination
    }
    else {
        $dest = $toDelete
    }

    # next check if the file contains path(s) for referenced (bitmap) file((s)
    $refCount = 0
    $content | Select-String -Pattern '(^.*)([A-Z]:\\.+$)' -AllMatches | ForEach-Object {
        # each '$_' automatic variable in here holds a MatchInfo object.
        # see: https://learn.microsoft.com/en-us/dotnet/api/microsoft.powershell.commands.matchinfo?view=pscore-6.0.0

        $prefix  = $_.Matches[0].Groups[1].Value   # get the prefix of the line (something like '0619154')
        $refPath = $_.Matches[0].Groups[2].Value   # get the bitmap file path

        if (Test-Path -Path $refPath -PathType Leaf) {
            Write-Host "Moving referenced file '$refPath' to '$dest'"
            Move-Item -Path $refPath -Destination $dest -Force
            # recreate the line to match the new location of the bitmap file
            Write-Host "Updating path in '$file' to '$refPath'"
            $refFile = Split-Path $refPath -Leaf
            $refPath = Join-Path -Path $dest -ChildPath $refFile
            $content[$_.LineNumber -1] = $prefix + $refPath
            $refCount++
        }
        else {
            Write-Warning "Referenced file '$refPath' not found"
        }
        if ($refCount) {
            # we handled referenced files, so write the new content back to the original file
            Set-Content -Path $file -Value $content -Force
        }
    }

    # finally move the file to its new destination
    Write-Host "Moving file '$file' to '$dest'"
    Move-Item -Path $file -Destination $dest -Force
}


EDIT


As per your comments:

I have tested this like below.

I created a couple of folders on my D: drive and put files in there:

+---Fnkraf
    \---Bitmaps
    |       PIC0053.BMP
    |       PIC0057.BMP
    |       PIC0571.BMP
    |       PIC0572.BMP
    |
    \---MasterFiles
            File1.txt
            File2.txt
            File3.txt

The Bitmaps folder contains the referenced bitmap files.
In the MasterFiles folder, I put these files:

File1.txt
This file is valid, because it contains both keyword patterns and has two referenced bitmap files. Both referenced files are present. These will go to the 24BHD folder.

24BHD
RuheEKG

01091521
0249153EKG 10 Sekunden
0619154D:\Fnkraf\Bitmaps\PIC0053.BMP
0619155D:\Fnkraf\Bitmaps\PIC0057.BMP
0118410HF

File2.txt
This file is valid, because it contains both keyword patterns and has two referenced bitmap files. One of which will spit out a warning because it cannot be found. These will go to the 24BHD folder.

24BHD
RuheEKG

01091521
0249153EKG 15 Sekunden
0719154D:\Fnkraf\Bitmaps\PIC0571.BMP
0719157D:\Fnkraf\Bitmaps\DOESNOTEXIST.BMP
0118410HG

File3.txt
This file is not valid, because it contains only one keyword pattern. It does have a findable referenced bitmap file. These should go to the toDelete folder

25BHD
RuheEKG

01091521
0249153EKG 17 Sekunden
0619154D:\Fnkraf\Bitmaps\PIC0572.BMP
0118410HG

After running the script, this is the result:

+---Fnkraf
    \---24BHD
    |       File1.txt
    |       File2.txt
    |       PIC0053.BMP
    |       PIC0057.BMP
    |       PIC0571.BMP
    |
    +---Bitmaps
    +---MasterFiles
    \---ToDelete
            File3.txt
            PIC0572.BMP

You can see both the destination 24BHD and the toDelete folder are created and master files File1.txt and File2.txt ended up in the destination, along with their referenced bitmap files.
File3.txt failed the pattern test as expected and was moved to the toDelete folder, again with the referenced bitmap file.

Now, if you open up the moved text files, you can see the referenced file paths have been updated to match the new location of the bitmaps.

File1.txt

24BHD
RuheEKG

01091521
0249153EKG 10 Sekunden
0619154D:\Fnkraf\24BHD\PIC0053.BMP
0619155D:\Fnkraf\24BHD\PIC0057.BMP
0118410HF

The same was done for the other files. The only reference that was NOT updated is the bitmap file that could not be found in File2.txt:

24BHD
RuheEKG

01091521
0249153EKG 15 Sekunden
0719154D:\Fnkraf\24BHD\PIC0571.BMP
0719157D:\Fnkraf\Bitmaps\DOESNOTEXIST.BMP
0118410HG

Hope that explains it all.

Sign up to request clarification or add additional context in comments.

6 Comments

Thank you very much for this helpful reply! I just dont understand what this codeblock does: if (Test-Path -Path $refPath -PathType Leaf) { Write-Host "Moving referenced file '$refPath' to '$dest'" Move-Item -Path $refPath -Destination $dest -Force # recreate the line to match the new location of the bitmap file Write-Host "Updating path in '$file' to '$refPath'" $refFile = Split-Path $refPath -Leaf $refPath = Join-Path -Path $dest -ChildPath $refFile $content[$_.LineNumber -1] = $prefix + $refPath $refCount++ Could you maybe help me out?
What it does is test if the referenced file inside the document (i.e.) C:\Transfer\CardioSoft\RuheEKG_LZRRToCWD\PIC0053.BMP can be found and if so, copy that file to the destination folder also. Because in the main file there is a number in front of the file path (i.e. 0619154) it captures that too. Next, it updates the file so the new file path of the referenced file is also updated to reflect the new location.
Okay, thank you very much! Youre helping me out immensely here. i have just one more thing. Everytime i execute the script, it moves all the files to "ToDelete" and it gives me a message saying "WARNING: REFERENCED FILE PATH NOT FOUND". Its as if it moves the files to the "ToDelete" folder before checking and relocating the referenced files. Do you have any ideas?
No wait, i get it now. Because i am testing this script, the path referencing the bitmap file isnt correct. Is there a way to just do it with the bitmap file name? Not the whole path?
@Fnkraf I have added explanation and test to my answer. The code updates the references to the new full path and file name of the referenced file in the master file. If you would rather have the referenced file name only (without the path), simply change the line $content[$_.LineNumber -1] = $prefix + $refPath to $content[$_.LineNumber -1] = $prefix + $refFile
|
0

A lot of questions come to my mind, like:

  • Is the filepath always on the end of a line?
  • Is there one filepath per file or could it be that there are more lines containing a path?

Assuming the answer on the above questions is yes the filepaths can be parsed from a file like this:

foreach($item in $hans) {
    $mat = $item | Select-String -Pattern '.:\\.+'
    foreach($m in $mat) {
        $m.Matches.Value
    }
}

.:\\.+ is a regular expression that searches the sequence of a driveletter, colon, backslash and grabs everything behind it on the same line.

Many times it takes some time before a parsing script like this works without errors. Regular expressions and Select-String are your friends, but it takes some time before you really know them :-) Check/write your RegEx on https://regex101.com/ I use it most of the time when writing regex.

Comments

0

If you have a list of patterns to match and you can guarantee that the patterns will always match and not accidentally match something else, you can use a switch statement with as many pattern matches and destinations as you like.

You might need to build the destination based on the source file name, I have crudely hard coded the destination paths in this case just as examples:

$hans = Get-ChildItem $source

foreach($item in $hans)
{
    switch($item.FullName)
    {
        { $_ -match '24BHD' }        { $destination = 'Z:\24BHD_Destination' }
        { $_ -match 'RuheEKG' }      { $destination = 'Z:\RuheEKG_Destination' }
        { $_ -match 'OtherPattern' } { $destination = 'Z:\OtherCode_Destination' }

        default { Write-Host "Match not found for file $($item.FullName)" }
    }

    Move-Item $item.FullName $destination
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.