PowerShell Copy-Item Method Fails - Brackets in Filename - windows

I am trying to use PowerShell (v.1) to copy over only files that match a pattern. The file naming convention is:
Daily_Reviews[0001-0871].journal
Daily_Reviews[1002-9887].journal
[...]
When I run it, the method "Copy-Item" complains:
Dynamic parameters for cmdlet cannot be retrieved. The specified wildcard pattern is not valid: Daily_Reviews[0001-0871].journal
+ Copy-Item <<<< $sourcefile $destination
The error is due to the "[" and "]" in the file names. When I remove the left and right brackets, it works as expected. But looks like PowerShell 1 doesn't have the -LiteralPath flag so is there another way to get Copy-Item to work in PowerShell 1 with file names that contain brackets?
$source = "C:\Users\Tom\"
$destination ="C:\Users\Tom\Processed\"
if(-not(Test-Path $destination)){mkdir $destination | out-null}
ForEach ($sourcefile In $(Get-ChildItem $source | Where-Object { $_.Name -match "Daily_Reviews\[\d\d\d\d-\d\d\d\d\].journal" }))
{
Copy-Item $sourcefile $destination
}

Well, after researching this more I found a workaround:
$src = [Management.Automation.WildcardPattern]::Escape($sourcefile)
Copy-Item $src $destination

$_ references the currently-referenced argument; you can't use it the way that you are here because that's outside the pipeline.
$source = "C:\Users\Tom\"
$destination ="C:\Users\Tom\Processed\"
if(-not(Test-Path $destination)){mkdir $destination | out-null}
ForEach ($sourcefile In $(Get-ChildItem $source | Where-Object { $_.Name -match "Daily_Reviews\[\d\d\d\d-\d\d\d\d\].journal" }))
{
Copy-Item -literalpath $sourcefile $destination
}

Related

Is there a way to rename files in bulk by the name of their folder?

I'm trying to write a PowerShell script that goes into every folder in a certain directory, and every child with the parent.
Example:
Z:\Folder1\File.txt
Z:\Folder1\Picture.jpeg
Z:\Folder1\Data.csv
Z:\Folder2\File.txt
Z:\Folder2\Picture.jpeg
Z:\Folder2\Data.csv
=
Z:\Folder1\Folder1.txt
Z:\Folder1\Folder1.jpeg
Z:\Folder1\Folder1.csv
Z:\Folder2\Folder2.txt
Z:\Folder2\Folder2.jpeg
Z:\Folder2\Folder2.csv
I have this so far
dir | rename-item -NewName {$_.name -replace *,"Folder1"}
Please let me know how I can loop through every folder, and how I can rename them based on the folder name.
Thanks!
This should do what you're asking:
Get-ChildItem Z:\Folder* -Directory | Get-ChildItem -File | Rename-Item -NewName { $_.DirectoryName.split('\')[-1] + $_.Extension } -WhatIf
Adjust the folder name filter (.\Folder*) as required and remove the -WhatIf if you're satisfied with the changes it will make.
Obviously this doesn't account for where you might have a name clash if there are two files in the folder with the same extension. The following would do that:
$FilesToRename = Get-ChildItem Z:\Folder* -Directory | Get-ChildItem -File
foreach ($FileToRename in $FilesToRename) {
$NewName = $FileToRename.DirectoryName.split('\')[-1] + $FileToRename.Extension
$NewFile = Join-Path $FileToRename.Directory $NewName
$i = 0
While (Test-Path $NewFile) {
$i = $i++
$NewName = $FileToRename.DirectoryName.split('\')[-1] + "[$i]" + $FileToRename.Extension
$NewFile = Join-Path $FileToRename.Directory $NewName
}
$FileToRename | Rename-Item -NewName $NewName
}

Removing trailing and ending blank spaces in folder and file names on Windows in bulk

I tried following Remove leading spaces in Windows file names but it's not working for my use case.
I have a lot of folders and filenames that either have a blank space at the front or at the end. How would I go about removing those spaces in bulk?
This was the command-line command I used after following the linked post:
for /R %A IN ("* ") do #for /F "tokens=*" %B IN ("%~nxA") do #ren "%A" "%B"
But it didn't work out.
Update: thank you to all who replied trying to help. I think there is just a Windows-level glitch in the file system. I ended up just having to manually create new folders without leading and trailing spaces and then dragging all the files over manually then renaming those to non-trailing and leading names as well.
It's unclear whether or not you want a PowerShell solution, but there's a reasonable assumption to be made you might.
If you wanted a PowerShell solution, you could try this:
function Test-LeadingTrailingWhitespace {
param(
[Parameter(Mandatory)]
[String]$String
)
$String[0] -eq ' ' -Or $String[-1] -eq ' '
}
Get-ChildItem -Path "<path_to_folder>" | ForEach-Object {
if ($_.PSIsContainer -And (Test-LeadingTrailingWhitespace -String $_.Name)) {
$Destination = Split-Path -Path $_.FullName -Parent
$NewName = $_.Name.Trim()
Move-Item -Path $_ -Destination (Join-Path -Path $Destination -ChildPath $NewName)
}
elseif (Test-LeadingTrailingWhitespace -String $_.BaseName) {
$Destination = Split-Path -Path $_.FullName -Parent
$NewName = $_.BaseName.Trim() + $_.Extension
Move-Item -Path $_ -Destination (Join-Path -Path $Destination -ChildPath $NewName)
}
}
To be on the safe side, you could add -WhatIf or -Confirm on the Move-Item cmdlet. The former will tell you what would have changed without that parameter without actually making any changes (like a 'dry run'). The latter will prompt you for confirmation before making each change, giving you a chance to validate incrementally and not make changes en masse from the moment you hit enter.
Trim() is a method available for all strings in PowerShell:
Returns a new string in which all leading and trailing occurrences of a set of specified characters from the current string are removed.
You can loop over files and folder and check if they actually have a leading or trailing whitespace before renaming, this would avoid errors like:
Rename-Item: Source and destination path must be different.
We can use the -match matching operator with a simple regex ^\s|\s$ (starts with whitespace or ends with whitespace - regex101 link for a simple example) to see if the file or folder should be renamed:
Get-ChildItem path\to\startingfolder -Recurse | ForEach-Object {
$newName = switch($_) {
# handle folders
{ $_.PSIsContainer -and $_.Name -match '^\s|\s$' } {
$_.Name.Trim()
break
}
# handle files
{ $_.BaseName -match '^\s|\s$' -or $_.Extension -match '^\s|\s$' } {
$_.BaseName.Trim() + $_.Extension.Trim()
break
}
# if none of the above conditions were true, continue with next item
Default {
return
}
}
Rename-Item -LiteralPath $_.FullName -NewName $newName
}
Personally, I'd do this in two steps to rename folders and files separately. This to overcome the problem that when a folder is renamed, the items inside that folder all have a new path.
Using switch -Force allows renaming items such as hidden or read-only files
Using -ErrorAction SilentlyContinue swallows the error when the new name is equal to the existing name
$rootPath = 'X:\thepath'
# first the folders and subfolders (deepest nesting first)
(Get-ChildItem -Path $rootPath -Directory -Recurse | Sort-Object FullName -Descending) |
Rename-Item -NewName {$_.Name.Trim()} -Force -ErrorAction SilentlyContinue
# next the files
(Get-ChildItem -Path $rootPath -File -Recurse) |
Rename-Item -NewName {'{0}{1}' -f $_.BaseName.Trim(), $_.Extension.Trim()} -Force -ErrorAction SilentlyContinue

Folder deleting after script ends

I am currently writing a script that takes a folder of files, moves the first file to a folder with a specific name, then move the rest to another folder with a number for a name.
My script works however it also moves the folder and renames it too. Which section of the code is causing this?
$path = "C:\Users\User1\Desktop\MergeTest\_First\"
$FileCount = Get-ChildItem -Path $path -File | Measure-Object | %{$_.Count}
$FirstFile = Get-ChildItem -Path $path -Force -File | Select-Object -First 1
$FinalReport = "C:\Users\User1\Desktop\MergeTest\___Final\TestOutput.xlsx"
Move-Item "C:\Users\User1\Desktop\MergeTest\_First\$FirstFile" $FinalReport
$Counter = 0;
Write-host $FileCount
for($Counter = 0; $Counter -lt $FileCount; $Counter++)
{
$FileInWork = Get-ChildItem -Path $path -Force -File | Select-Object -First 1
move-item "C:\Users\User1\Desktop\MergeTest\_First\$FileInWork" "C:\Users\User1\Desktop\MergeTest\__Second\$Counter.xlsx"
Write-host "File Moved"
}
What you could do is specify the -Include *.txt condition to your move-item commands so it is only to move just .txt, .log, or whatever file type you're moving and leave the folder how it is.
I believe your code could do with some cleaning up. Now you are executing Get-ChildItem 3 times, where using it once is enough.
Also, you should try and use the Join-Path rather than constructing the path and filenames yourself.
Especially where you do "C:\Users\User1\Desktop\MergeTest\_First\$FileInWork", you should realize that Get-ChildItem returns FileInfo and/or DirectoryInfo objects; not strings.
Anyway, the below code should do what you want:
# define the path where all other paths are in
$rootPath = "C:\Users\User1\Desktop\MergeTest"
# create the working paths using the common root folder path
$filesPath = Join-Path -Path $rootPath -ChildPath '_First'
$firstDestination = Join-Path -Path $rootPath -ChildPath '___Final'
$secondDestination = Join-Path -Path $rootPath -ChildPath '__Second'
# test if the destination folders exist and if not create them
if (!(Test-Path -Path $firstDestination -PathType Container)) {
Write-Host "Creating folder '$firstDestination'"
$null = New-Item -Path $firstDestination -ItemType Directory
}
if (!(Test-Path -Path $secondDestination -PathType Container)) {
Write-Host "Creating folder '$secondDestination'"
$null = New-Item -Path $secondDestination -ItemType Directory
}
# get an array of all FileInfo objects in $filesPath
# you could consider adding -Filter '*.xlsx' here..
$allFiles = Get-ChildItem -Path $filesPath -Force -File
Write-Host 'Total number of files found: {0}' -f $allFiles.Count
# move the files
for ($i = 0; $i -lt $allFiles.Count; $i++) {
if ($i -eq 0) {
# the first file should go in the $firstDestination folder with specified name
$target = Join-Path -Path $firstDestination -ChildPath 'TestOutput.xlsx'
}
else {
# all other files go to the $secondDestination folder
# each file should have the index number as name
$target = Join-Path -Path $secondDestination -ChildPath ('{0}.xlsx' -f ($i + 1))
}
$allFiles[$i] | Move-Item -Destination $target -Force -WhatIf
}
Hope that helps
Remove the -WhatIf if you are satisfied with whatever the output on console shows.
P.S. I really think you should edit your question and change its title, because nothing in the question has to do with Folder deleting after script ends..

How can I move items without hardcoded variables

I want to move items with the .txt extension but not like this, hard-coded. I want to select from a folder all files with some extension and move them into a folder with the same name as extension. And I want to do that for all the extensions in the directory.
Any ideas?
Thank you!
Take a look at my code but I did it with hardcoded variables
$variable=Get-ChildItem -Path "C:\somePath"
foreach ($variables in $variable)
{
if($extension=($variables | Where {$_.extension -like ".txt"}))
{
New-Item -ItemType Directory -Path "C:\somePath\text"
$extension | Move-Item -Destination "C:\somePath\text"
}
}
Although this solution is not as clean-looking as the other solution, it does handle the case where the destination folders don't already exist. It also moves files that may contain special characters like []. It also explicitly ignores files with no extension since no requirement was given for those. The amount of looping is minimized through the use of Group-Object.
$Path = "C:\Somepath"
$files = Get-ChildItem -Path $Path -File |
Group-Object -Property {($_.extension |
Select-String -Pattern "[^. ]+").matches.value
}
Foreach ($ExtGroup in $files) {
$Destination = "$Path\$($ExtGroup.Name)"
if (!(Test-Path -Path $Destination -PathType Container)) {
$null = New-Item -Path $Destination -Type Directory
}
Move-Item -LiteralPath $ExtGroup.Group -Destination $Destination -Force
}
I believe this will do:
$variable = Get-ChildItem -Path "C:\somePath"
foreach ($v in $variable){
$dest = '{0}\{1}' -f $v.DirectoryName, ($v.Extension -replace '^\.')
$v | Move-Item -Destination $dest -Force
}

Moving all folders and subfolders from one drive to another drive using Powershell

Iam using Powershell script to Move folders from one Drive to another.
Here is what i have tried.
Get-ChildItem -Recurse "C:\Personal" | where-object {$_.lastwritetime -gt '5-25-2015'} | foreach {move-item "$($_.FullName)" "D:\Personal"}
This is working if i am moving files within same drive i.e either from cdrive to c drive or d drive to d drive.
But this is not working when iam trying to move from c drive to d drive,...iam getting error like
Move-Item : The file exists.
At line:1 char:113
+ Get-ChildItem -Recurse "C:\Development" | where-object {($_.lastwritetime -lt (get-date))} | foreach {move-item <<<<
"$($_.FullName)" "D:\Development1"}
+ CategoryInfo : WriteError: (C:\Development\test3.txt:FileInfo) [Move-Item], IOException
+ FullyQualifiedErrorId : MoveFileInfoItemIOError,Microsoft.PowerShell.Commands.MoveItemCommand
Please correct me..
What means "this is not working"?
The recurse flag seems to indicate you want to copy a directory structure. This will only work if your target directory already has the same structure as your source. If not, you have to create it along the way. Something like this would work:
function Move-Directories
{
param (
[parameter(Mandatory = $true)] [string] $source,
[parameter(Mandatory = $true)] [string] $destination
)
try
{
Get-ChildItem -Path $source -Recurse -Force |
Where-Object { $_.psIsContainer } |
ForEach-Object { $_.FullName -replace [regex]::Escape($source), $destination } |
ForEach-Object { $null = New-Item -ItemType Container -Path $_ }
Get-ChildItem -Path $source -Recurse -Force |
Where-Object { (-not $_.psIsContainer) -and ($_.lastwritetime -ge (get-date)) } |
Move-Item -Force -Destination { $_.FullName -replace [regex]::Escape($source), $destination }
}
catch
{
Write-Host "$($MyInvocation.InvocationName): $_"
}
}
With a call like this:
Move-Directories "c:\personal" "d:\personal"

Resources