Home > Back-end >  How to pipe Rename-Item into Move-Item (powershell)
How to pipe Rename-Item into Move-Item (powershell)

Time:07-21

I'm in the process of writing up a PowerShell script that can take a bunch of .TIF images, rename them, and place them in a new folder structure depending on the original file name.

For example, a folder containing the file named: ABC-ALL-210316-0001-3001-0001-1-CheckInvoice-Front.TIF

would be renamed to "00011CIF.TIF", and placed in the following folder: \20220316\03163001\

I've been trying to put together a code to perform this task, and I got one to work where I had two different "ForEach" methods. One would do a bunch of file renaming to remove "-" and shorten "CheckInvoiceFront" to "CIF" and such. Then the second method would again pull all .TIF images, create substrings of the image names, and create folders from those substrings, and then move the image to the new folder, shortening the file name. Like I said, it worked... but I wanted to combine the ForEach methods into one process. However, each time I try to run it, it fails for various reasons... I've tried to change things around, but I just can't seem to get it to work.

Here's the current (non-working) code:

# Prompt user for directory to search through
$sorDirectory = Read-Host -Prompt 'Input source directory to search for images: '
$desDirectory = Read-Host -Prompt 'Input target directory to output folders: '
Set-Location $sorDirectory

# Check directory for TIF images, and close if none are found
Write-Host "Scanning "$sorDirectory" for images... "
$imageCheck = Get-ChildItem -File -Recurse -Path $sorDirectory -include '*.tif'
$imageCount = $imageCheck.count
if ($imageCount -gt 0) {
    Write-Host "Total number of images found: $imageCount"
    ""
    Read-Host -Prompt "Press ENTER to continue or CTRL C to quit"
    $count1=1;

    # Rename all images, removing "ABCALL" from the start and inserting "20", and then shorten long filetype names, and move files to new folders with new names
    Clear-Host
    Write-Host "Reformatting images for processing..."
    ""
    Get-ChildItem -File -Recurse -Path $sorDirectory -include '*.tif' |
    ForEach-Object {
        Write-Progress -Activity "Total Formatted Images: $count1/$imageCount" -Status "0--------10%--------20%--------30%--------40%--------50%--------60%--------70%--------80%--------90%-------100" -CurrentOperation $_ -PercentComplete (($count1 / $imageCount) * 100)
        Rename-Item $_ -NewName $_.Name.Replace("-", "").Replace("ABCALL", "20").Replace("CheckInvoiceFront", "CIF").Replace("CheckInvoiceBack", "CIB").Replace("CheckFront", "CF").Replace("CheckBack", "CB") |Out-Null
        $year = $_.Name.SubString(0, 4)
        $monthday = $_.Name.Substring(4,4)
        $batch = $_.Name.SubString(12, 4)
        $fulldate = $year $monthday
        $datebatch = $monthday $batch
        $image = $_.Name.SubString(16)
        $fullPath = "$desDirectory\$fulldate\$datebatch"
        if (-not (Test-Path $fullPath)) { mkdir $fullPath |Out-Null }
        Move-Item $_ -Destination "$fullPath\$image" |Out-Null
        $count1  
    }

    # Finished
    Clear-Host
    Write-Host "Job complete!"
    Timeout /T -1
}

# Closes if no images are found (likely bad path)
else {
    Write-Host "There were no images in the selected folder.  Now closing..."
    Timeout /T 10
    Exit
}

Usually this results in an error stating that it's can't find the path of the original file name, as if it's still looking for the original non-renamed image. I tried adding some other things, but then it said I was passing null values. I'm just not sure what I'm doing wrong.

Note that if I take the everything after the "Rename-Item" (starting with "$year =") and have that in a different ForEach method, it works. I guess I just don't know how to make the Rename-Item return its results back to "$_" before everything else tries working on it. I tried messing around with "-PassThru" but I don't think I was doing it right.

Any suggestions?

CodePudding user response:

  • As Olaf points out, situationally you may not need both a Rename-Item and a Move-Item call, because Move-Item can rename and move in single operation.

  • That said, Move-Item does not support implicit creation of the target directory to move a file to, so in your case you do need separate calls.


You can use Rename-Item's -PassThru switch to make it output a System.IO.FileInfo instance (or, if a directory is being renamed, a System.IO.DirectoryInfo instance) representing the already renamed file; you can directly pass such an instance to Move-Item via the pipeline:

Get-ChildItem -File -Recurse -Path $sorDirectory -include '*.tif' |
ForEach-Object {
    # ...
    # Use -PassThru with Rename-Item to output a file-info object describing
    # the already renamed file.
    $renamedFile = $_ | Rename-Item -PassThru -NewName $_.Name.Replace("-", "").Replace("ABCALL", "20").Replace("CheckInvoiceFront", "CIF").Replace("CheckInvoiceBack", "CIB").Replace("CheckFront", "CF").Replace("CheckBack", "CB")
    # ...
    # Pass $renamedFile to Move-Item via the pipeline.
    $renamedFile | Move-Item -Destination "$fullPath\$image"
    # ... 
}

As for your desire to:

make the Rename-Item return its results back to "$_"

While PowerShell doesn't prevent you from modifying the automatic $_ variable, it is better to treat automatic variables as read-only.
Therefore, a custom variable is used above to store the output from Rename-Item -PassThru

CodePudding user response:

You need -passthru and -destination:

rename-item file1 file2 -PassThru | move-item -Destination dir1
  • Related