Skip to content

PowerShell Moving Files

How to use PowerShell's Move-Item cmdlet to move and rename files between directories, drives, or network shares with wildcards, error handling, and cross-drive mechanics.

Moving files between directories or storage drives is performed using the Move-Item cmdlet (aliases: move, mv, mi). It relocates files and folders from one location to another, removing them from the source path entirely. It supports wildcard patterns for bulk moves and works transparently across different drives, including mapped network drives, PSDrives, and UNC paths.

Move-Item Basics

Basic Syntax

Move-Item -Path <String[]> -Destination <String> [-Force] [-PassThru] [-WhatIf] [-Confirm]
Move-Item -Path "C:\Downloads\report.pdf" -Destination "C:\Archive\report.pdf"
When the file is moved, there is no output to indicate success. The operation occurs silently.

Verify the move:

Test-Path "C:\Downloads\report.pdf"
Test-Path "C:\Archive\report.pdf"
False
True

Where:

  • -Path specifies the source file to move.
  • -Destination specifies the target location, which can be a full file path or an existing folder.
  • -PassThru instructs Move-Item to emit the resulting FileInfo object to the pipeline.
  • -Force allows overwriting read-only files in the destination or replacing files if conflicts exist.

Moving to a Folder Without Renaming

If -Destination points to an existing folder rather than a full file path, the file keeps its original name.

Move-Item -Path "C:\Downloads\report.pdf" -Destination "C:\Archive\"

Relocating and Renaming a Single File

$SourceFile = "$env:TEMP\Staging\app.log"
$DestFolder = "$env:TEMP\Archive"

if (-not (Test-Path -Path $DestFolder -PathType Container)) {
    $null = New-Item -ItemType Directory -Path $DestFolder -Force
}

Move-Item -Path $SourceFile -Destination $DestFolder -PassThru
Outputtext
    Directory: C:\Users\Tom\AppData\Local\Temp\Archive

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a---           10/24/2024  11:30 PM          10240 app.log

Moving Files with Wildcards

Move-Item accepts wildcard patterns (*, ?) in the -Path parameter to move batches of files matching specific naming conventions or extensions.

When moving multiple files with wildcards, the -Destination parameter must be an existing directory.

Move-Item -Path "C:\Downloads\*.pdf" -Destination "C:\Archive\"
Get-ChildItem "C:\Archive"
    Directory: C:\Archive

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a---           3/14/2024   8:30 AM         102400 invoice.pdf
-a---           3/14/2024   8:31 AM          88320 report.pdf

Batch Move Example

$SourceDir = "$env:TEMP\Processing"
$TargetDir = "$env:TEMP\Completed"

Move-Item -Path "$SourceDir\*.csv" -Destination $TargetDir -Force

Get-ChildItem -Path $TargetDir -Filter "*.csv" | Select-Object -Property Name, Length
Name               Length
----               ------
customers-q3.csv    15420
orders-q3.csv       84200

Where:

  • "$SourceDir\*.csv" matches all CSV files in the source directory.
  • -Destination $TargetDir moves all matching items into the destination folder.
  • -Force allows overwriting read-only files in the destination or replacing files if conflicts exist.

Combining with Get-ChildItem and Where-Object for Precision

For more selective moves than a simple wildcard allows, pipe filtered results into Move-Item.

Get-ChildItem -Path "C:\Downloads" -Filter "*.csv" |
    Where-Object { $_.LastWriteTime -gt (Get-Date).AddDays(-1) } |
    Move-Item -Destination "C:\Data\Recent\"

Where only .csv files modified within the last day are moved, leaving older files untouched.

Cross-Drive Moves

The Move-Item cmdlet works seamlessly across various drive types, including local drives, mapped network drives, and PSDrives, without requiring any extra syntax.

Move-Item -Path "C:\Projects\output.zip" -Destination "D:\Backups\output.zip"
Test-Path "C:\Projects\output.zip"
Test-Path "D:\Backups\output.zip"
False
True

Moving to a Network Share (UNC Paths)

Move-Item -Path "C:\Reports\quarterly.xlsx" -Destination "\\FileServer01\Shared\Reports\quarterly.xlsx"

Where, even though the source (C:\) and destination (D:\ or a UNC share) are on different physical or logical drives, Move-Item handles the underlying copy-then-delete operation automatically.

Same-Drive vs. Cross-Drive Mechanics

Same-Volume Move (C:\Staging ──► C:\Archive):
[File Clusters on Disk] (Unchanged)
  └── Master File Table (MFT) Pointer Updated ──► Instantaneous (Atomic)

Cross-Volume Move (C:\Staging ──► D:\Archive):
[File Clusters on C:] ──► Read Data ──► Write to D: ──► Verify ──► Delete from C:
Dimension Same-Volume Move (C: to C:) Cross-Volume Move (C: to D: or UNC)
Operation Type Metadata pointer update (atomic). Data copy followed by source deletion.
Execution Speed Near instantaneous regardless of file size. Dependent on network/disk transfer throughput.
Interruption Risk Low; filesystem updates pointer in a single transaction. Moderate; if aborted mid-transfer, destination may hold partial files.
NTFS Permissions Retains original permissions by default. Inherits permissions of the destination parent folder.

Practical Examples

Archiving Processed Files

A common automation pattern moves files out of an "incoming" folder once they've been processed, preventing them from being processed again on the next script run.

$incomingFolder = "C:\Data\Incoming"
$archiveFolder = "D:\Data\Archive"

if (-not (Test-Path $archiveFolder)) {
    New-Item -ItemType Directory -Path $archiveFolder | Out-Null
}

Get-ChildItem -Path $incomingFolder -Filter "*.csv" | ForEach-Object {
    Write-Output "Processing $($_.Name)..."

    $destination = Join-Path $archiveFolder $_.Name
    Move-Item -Path $_.FullName -Destination $destination
    Write-Output "Archived $($_.Name) to $archiveFolder"
}
Processing january.csv...
Archived january.csv to D:\Data\Archive
Processing february.csv...
Archived february.csv to D:\Data\Archive

Where:

  • Moving each file to a separate drive (D:\) after processing ensures the Incoming folder only ever contains new, unprocessed files.
  • Join-Path safely builds the destination path without manual string concatenation.

Automated Staging to Storage Ingestion Engine

The following production script processes an inbound staging folder, creates a destination archive directory if needed, moves all matching log and text files, and emits a structured audit report using -PassThru:

param (
    [string]$SourceDirectory = "$env:TEMP\SourceDrop",
    [string]$TargetDirectory = "$env:TEMP\ArchiveStore"
)

if (-not (Test-Path -Path $SourceDirectory -PathType Container)) {
    Write-Error "Source directory '$SourceDirectory' does not exist."
    exit 1
}

if (-not (Test-Path -Path $TargetDirectory -PathType Container)) {
    Write-Output "Destination folder not found. Creating: $TargetDirectory"
    $null = New-Item -ItemType Directory -Path $TargetDirectory -Force
}

$FilesToMove = Get-ChildItem -Path $SourceDirectory -File |
    Where-Object { $_.Extension -in @(".log", ".txt", ".csv") }

if ($FilesToMove.Count -eq 0) {
    Write-Output "No eligible files found to relocate."
    exit 0
}

Write-Output "Relocating $($FilesToMove.Count) file(s) to destination..."

$MoveResults = foreach ($file in $FilesToMove) {
    try {
        $movedItem = Move-Item -Path $file.FullName -Destination $TargetDirectory -Force -PassThru -ErrorAction Stop

        [PSCustomObject]@{
            FileName       = $movedItem.Name
            SizeBytes      = $movedItem.Length
            SourcePath     = $file.DirectoryName
            NewLocation    = $movedItem.DirectoryName
            Status         = "Success"
            MovedTimestamp = (Get-Date -Format "yyyy-MM-dd HH:mm:ss")
        }
    }
    catch {
        [PSCustomObject]@{
            FileName       = $file.Name
            SizeBytes      = $file.Length
            SourcePath     = $file.DirectoryName
            NewLocation    = $TargetDirectory
            Status         = "Failed: $($_.Exception.Message)"
            MovedTimestamp = (Get-Date -Format "yyyy-MM-dd HH:mm:ss")
        }
    }
}

$MoveResults | Format-Table -AutoSize

Where:

  • Test-Path validates both the source directory and ensures the target directory is available.
  • Where-Object { $_.Extension -in ... } filters for specific supported file types.
  • Move-Item -PassThru -ErrorAction Stop moves each file and captures the resulting FileInfo object.
  • try/catch catches locked-file errors or permission failures without aborting the loop for remaining files.
  • [PSCustomObject] builds an audit trail of all moved files.

Execute the script from the console:

pwsh ./relocate-staging-files.ps1
Destination folder not found. Creating: C:\Users\Tom\AppData\Local\Temp\ArchiveStore
Relocating 2 file(s) to destination...

FileName         SizeBytes SourcePath                                 NewLocation                                  Status  MovedTimestamp
--------         --------- ----------                                 -----------                                  ------  --------------
app-trace.log         4210 C:\Users\Tom\AppData\Local\Temp\SourceDrop C:\Users\Tom\AppData\Local\Temp\ArchiveStore Success 2024-10-24 23:58:10
orders-batch.csv     18450 C:\Users\Tom\AppData\Local\Temp\SourceDrop C:\Users\Tom\AppData\Local\Temp\ArchiveStore Success 2024-10-24 23:58:10

Quick Reference

Task Command
Move a single file Move-Item -Path "C:\Temp\file.txt" -Destination "C:\Archive\file.txt"
Move into an existing folder Move-Item -Path "C:\Temp\file.txt" -Destination "C:\Archive\"
Move and rename Move-Item -Path "C:\Temp\old.txt" -Destination "C:\Archive\new.txt"
Move with wildcards Move-Item -Path "C:\Downloads\*.pdf" -Destination "C:\Archive\"
Move with pipeline filter Get-ChildItem ... | Where-Object { ... } | Move-Item -Destination ...
Preview a move Move-Item -Path ... -Destination ... -WhatIf
Overwrite destination conflicts Move-Item -Path ... -Destination ... -Force
Emit moved object info Move-Item -Path ... -Destination ... -PassThru
Navigation