0

I'm looking for a PowerShell script which can find the files (N30008xx.txt, N30005xx.txt) from the source directory and copy them to the destination directory by creating a folder with the same name of the file's modification date.

I'm able to run the below script which creates the folder by files modified date.

$p = "Filesourcepath"

Get-ChildItem -Path $p |
    Where-Object { ! ($_.PSIsContainer) } | 
    ForEach-Object {
        $newDir = Join-Path $p ($_.LastWriteTime).ToString("yyyy-MM-dd")
        New-Item -Path $newDir -ItemType Directory -ErrorAction SilentlyContinue
        $_ | Move-Item -Destination $newDir
    }
Ansgar Wiechers
  • 193,178
  • 25
  • 254
  • 328
Anil
  • 11
  • 4

2 Answers2

1

Your code should work in principle. (As of this writing, there's confusion over copying vs. moving, and the aspect of matching only select files is missing.)

Below is a streamlined version of your code, which however, does not explain your symptom - you need to provide more information for us to diagnose your problem.


The streamlined code below:

  • takes advantage of the PSv3+ -File Get-ChildItem parameter to limit matching to files (as opposed to directories) - this saves the need for Where-Object { ! $_.PSIsContainer }.

  • uses -LiteralPath to pass the literal $dir path; while -Path (which is also the positional default) often works fine too, it's important to note that it interprets is argument as a wildcard expression, which can have unexpected results).

  • uses -Filter to provide the file mask (wildcard expression); this is generally preferable to using the -Path parameter, because it filters at the source (Windows API call) and is therefore faster, which can make a noticeable difference when processing large directories.
    Caveat: the wildcard language supported in the -Filter argument is more limited than PowerShell's and also burdened with legacy quirks; in short: sticking with * and ? should be fine; for the full story, see this well-researched answer.

  • uses -Force instead of -ErrorAction SilentlyContinue in order to either create a directory or use a preexisting one.

    • Note that New-Item -ItemType Directory -Force returns a [System.IO.DirectoryInfo] instance in both scenarios (either the newly created directory or the preexisting one), which the code takes advantage of.
# Create sample dir. with 2 sample files in it.
$tmpDir = New-Item -Force -Type Directory tmpDir
New-Item -Type File -Force -Path ('N30008xx.txt', 'N30005xx.txt' -replace '^', "$($tmpDir.FullName)/")

$dir = $tmpDir
$fileMask = 'N*.txt'

Get-ChildItem -File -LiteralPath $dir -Filter $fileMask | ForEach-Object {
  $newDir = Join-Path $dir $_.LastWriteTime.ToString("yyyy-MM-dd")
  $_ | Move-Item -Destination (New-Item -ItemType Directory -Force $newDir) 
}

Caveat re generalization of this code:

  • You're creating the target subdirectories inside the source directory.

  • If you were to use Get-ChildItem -Recurse to process the source directory recursively, you'd end up processing matching files twice: first when moving them, and then again when finding them in their moved-to location.
    (In this particular case this would only cause an inefficiency, however, because processing the already-moved files attempts to move them into the directory where they already are, which is a quiet no-op.)

mklement0
  • 382,024
  • 64
  • 607
  • 775
0

Here is a modified version of your PowerShell that should work.

Note: Your destination directory cannot be located under the source directory otherwise you will have a forever recursive move.

$p = pwd
$dst = "c:/tmp/testdir"

Get-ChildItem -Path $p | Where-Object  {
    $_.PSIsContainer -eq $false
} | ForEach-Object {
    $newdir = Join-Path -Path $dst -ChildPath ($_.LastWriteTime).ToString("yyyy-MM-dd")
    if (!(Test-Path -Path $newdir)) {
        Write-Host "Create directory $newdir"
        New-Item -Path $newdir -ItemType Directory
    }
    Write-Host "Copy file $_"
    Move-Item -Path $_ -Destination $newdir
}
John Hanley
  • 74,467
  • 6
  • 95
  • 159
  • it doesn't copy the files! – Anil Jan 28 '18 at 10:56
  • While the warning re creating new files inside a directory being enumerated is worth heeding in general, there is _no_ problem in this case, because `-Recurse` is not being used. Careful with `Move-Item -Path $_`: it will (regrettably) only work if `$p` also happens to be the current location, because `$_` is effectively bound by _filename only_; either use `$_.FullName` or use the pipeline - see [this answer](https://stackoverflow.com/a/40494173/45375) of mine (As an aside: better to use `-LiteralPath`). – mklement0 Jan 28 '18 at 17:52