I am trying to rename certain files and then copy them to a backup location as below:
gci $src `
| ?{!$_.psiscontainer -and $_.extension.length -eq 0 -and $_ -match "tmp_\d{1}$"} `
| %{ ren -path $_.fullname -new ($_.name + ".ext") } `
| %{ cpi -path $_.fullname -dest $bkup -force}
The renaming part is working fine. But the renamed files are not being copied over to the backup location. What I am doing wrong here?
Rename-Item doesn't return anything so there is nothing to pipe to Copy-Item. You could just put both commands in the for each block together:
gci $src `
| ?{!$_.psiscontainer -and $_.extension.length -eq 0 -and $_ -match "tmp_\d{1}$"} `
| %{ $renamedPath = $_.FullName + ".ext"; `
ren -path $_.FullName -new $renamedPath; `
cpi -path $renamedPath -dest $bkup -force }
By default renamed items will not be pushed back onto the pipeline, use the -PassThru switch to pass them on:
gci $src `
| ?{!$_.psiscontainer -and $_.extension.length -eq 0 -and $_ -match "tmp_\d{1}$"} `
| %{ ren -path $_.fullname -new ($_.name + ".ext") -PassThru } `
| %{ cpi -path $_.fullname -dest $bkup -force}
You accomplish both in one operation with move-item.
gci $src
| ?{!$_.psiscontainer -and $_.extension.length -eq 0 -and $_ -match "tmp_\d{1}$"}
| %{
$newname = $_.Name + ".ext"
move-item -path $_.FullName -dest "$bkup\$newname"
}
One liner:
gci $src | ?{!$_.psiscontainer -and !$_.extension -and $_ -match 'tmp_\d$'} | move-item -dest {"$bkup\$($_.Name + '.ext')"}
Related
How can I run both 'rename' command and 'hidden' command after I filtered files using Where condition. Each command runs well on its own when placed first, but the second one is neglected.
Get-ChildItem -Recurse -Force | Where {(
$_.Extension -ne ".mp3" -and
$_.Extension -ne ".wmv" )} |
Set-ItemProperty -Name Attributes -Value "Hidden" -WhatIf |
Rename-Item -NewName {$_.Name -replace $_.Extension, -join($_.Extension, ".notmusic")} -
WhatIf
For-Each:
Get-ChildItem -Recurse -Force | Where {( $_.Extension -ne ".mp3" -and $_.Extension -ne ".wmv" )} | ForEach-Object {
$_ | Set-ItemProperty -Name Attributes -Value "Hidden" -WhatIf
$_ | Rename-Item -NewName { $_.Name -replace $_.Extension, -join($_.Extension, ".notmusic")} -WhatIf
}
I use ForEach-Object
Get-ChildItem -Recurse -Force | Where {( $_.Extension -ne ".mp3" -and $_.Extension -ne ".wmv" )} | ForEach-Object {
Set-ItemProperty -Name Attributes -Value "Hidden" -Path $_.FullName -WhatIf
Rename-Item -Path $_.FullName -NewName "$($_.Name).notmusic" -WhatIf
}
I add in Set-ItemProperty -Path
Set-ItemProperty -Name Attributes -Value "Hidden" -Path $_.FullName -WhatIf
i change Rename-Item -Path and -NewName
Rename-Item -Path $_.FullName -NewName "$($_.Name).notmusic"
I see the first couple stanzas grab files in your directory that are not mp3 or wmv files.
Get-ChildItem -Recurse -Force | Where {($_.Extension -ne ".mp3" -and $_.Extension -ne ".wmv")}
That part is good. But since you're taking two actions on each file, you should then pipe to a ForEach-Object in the form of
... | ForEach-Object {
# do something
# do something else
}
And since you're not piping directly into the Set-ItemProperty cmdlet, you'll pass it the path of the filename as such:
Set-ItemProperty -Path $_.FullName -Name Attributes -Value "Hidden"
Then we get to your rename command. Looks like you want the name to be filename.notmusic. Again, we'll need to pass the file path to the cmdlet. Also note how using subexpressions is a clean/nice way of defining the new name
Rename-Item -Path $_.FullName -NewName "$($_.BaseName).notmusic"
Ok so put it all together and we have:
Get-ChildItem -Recurse -Force | Where-Object {($_.Extension -ne ".mp3" -and $_.Extension -ne ".wmv")} | ForEach-Object {
Set-ItemProperty -Path $_.FullName -Name Attributes -Value "Hidden" -WhatIf
Rename-Item -Path $_.FullName -NewName "$($_.BaseName).notmusic" -WhatIf
}
I am trying to copy all files recursively from a:\ to b:\, except those whose metadata is present in a:\list.txt. The list.txt pattern is LastWriteTimeYYYY-MM-DD HH:MM:SS,size,.fileextension, for example:
2001-01-31 23:59:59,12345,.doc
2001-01-31 23:59:59,12345,.txt
2001-01-31 23:59:00,456,.csv
...so any and all files, anywhere in the a:\ dir tree, matching these metadata should not be copied.
I seem to be having trouble with the Where-Object in order to exclude the items on the list.txt, but copy everything else:
$Source = "C:\a"
$Target = "C:\b"
$List = Import-Csv list.txt -Header LastWriteTime,Size,Name
$Hash = #{}
ForEach ($Row in $List){
$Key = ("{0},{1},.{2}" -F $Row.LastWriteTime,$Row.Size,$Row.Name.Split('.')[-1].ToLower())
IF (!($Hash[$Key])) {$Hash.Add($Key,$Row.Name)}
}
$Hash | Format-Table -Auto
Get-Childitem -Path $Source -Recurse -File | Where-Object {$Hash -eq $Hash[$Key]}| ForEach-Object {$Key = ("{0},{1},{2}" -F ($_.LastWriteTime).ToString('yyyy-MM-dd HH:mm:ss'),$_.Length,$_.Extension.ToLower())
#$Key
If ($Hash[$Key]){
$Destination = $_.FullName -Replace "^$([RegEx]::Escape($Source))","$Target"
If (!(Test-Path (Split-Path $Destination))){MD (Split-Path $Destination)|Out-Null}
$_ | Copy-Item -Destination $Destination
}
}
I propose you a simplification of your code :
$Source = "C:\a\"
$Target = "C:\b\"
New-Item -ItemType Directory $Target -Force | Out-Null
$List = Import-Csv list.txt -Header LastWriteTime,Length,Extension
Get-Childitem $Source -Recurse -File | %{
$File=$_
$exist=$List | where {$_.LastWriteTime -eq $File.LastWriteTime.ToString('yyyy-MM-dd HH:mm:ss') -and $_.Length -eq $File.Length -and $_.Extension -eq $File.Extension} | select -first 1
if ($exist -ne $null) {continue}
New-Item -ItemType Directory $File.DirectoryName.Replace($Source, $Target) -Force | Out-Null
Copy-Item $File.FullName $File.FullName.Replace($Source, $Target) -Force
}
I'm trying to log data surrounding the renaming of files using the following script. The only problem is that the log file contains files that were not renamed due to 'access denied' errors when attempting to rename. I need to figure out how to only create log entries for files that were SUCCESSFULLY renamed or pipe the failed renames to a different log file. I'd also like the total number of files renamed listed at the top of the log file if at all possible(ie 'xxx files were renamed') I appreciate any suggestions for getting this to work using powershell v2.
$drivesArray = Get-PSDrive -PSProvider 'FileSystem' | select -Expand Root
foreach ($drive in $drivesArray) {
Get-ChildItem $drive | Where-Object {
$_.FullName -notlike "${Env:WinDir}*" -and
$_.FullName -notlike "${Env:ProgramFiles}*"
} | ForEach-Object {
Get-ChildItem $_.FullName -Recurse -ErrorAction SilentlyContinue
} | Where-Object {
-not $_.PSIsContainer -and
$_.Extension -notmatch '^\.(xxx|exe|html)$'
} | ForEach-Object {
$newName = $_.FullName + '.xxx';
Rename-Item -Path $_.FullName -NewName ($_.FullName + '.xxx') -ErrorAction SilentlyContinue
Add-Content c:\temp\renameLog.txt -Value $('{0} {1} {2} {3}' -f $(Get-Date),$_.fullname,$_.name,$newName )
}
}
Here is an example, untested of course.
$success = 0
$failed = 0
$drivesArray = Get-PSDrive -PSProvider 'FileSystem' | select -Expand Root
foreach ($drive in $drivesArray) {
Get-ChildItem $drive | Where-Object {
$_.FullName -notlike "${Env:WinDir}*" -and
$_.FullName -notlike "${Env:ProgramFiles}*"
} | ForEach-Object {
Get-ChildItem $_.FullName -Recurse -ErrorAction SilentlyContinue
} | Where-Object {
-not $_.PSIsContainer -and $_.Extension -notmatch '^\.(xxx|exe|html)$' -and $_.Name -notmatch '^renameLog.txt|^renameLog_failed.txt'
} | ForEach-Object {
try {
$newName = $_.FullName + '.xxx';
Rename-Item -Path $_.FullName -NewName ($_.FullName + '.xxx') -ErrorAction Stop
Add-Content c:\temp\renameLog.txt -Value $('{0} {1} {2} {3}' -f $(Get-Date),$_.fullname,$_.name,$newName )
$success ++
} catch [exception] {
Add-Content c:\temp\renameLog_failed.txt -Value $('{0} {1} {2} {3}' -f $(Get-Date),$_.fullname,$_.name,$newName )
$failed ++
$error.Clear()
}
}
}
Add-Content "c:\temp\renameLog.txt" -Value $("Total: " + $success)
Add-Content "c:\temp\renameLog_failed.txt" -Value $("Total: " + $failed)
I want this to recurse every directory and create a zero-byte file for every file using the same name as the file with the extension .xxx added. I was thinking New-Item would be good to use here but I cant seem to get it working right.
Here is what I've tried with no success in PS version 2:
$drivesArray = Get-PSDrive -PSProvider 'FileSystem' | select -Expand Root
foreach ($drive in $drivesArray) {
ls "$drive" | where {
$_.FullName -notlike "${Env:WinDir}*" -and
$_.FullName -notlike "${Env:ProgramFiles}*"
} | ls -ErrorAction SilentlyContinue -recurse | where {
-not $_.PSIsContainer -and
$_.Extension -notmatch '\.xxx|\.exe|\.html'
} | New-Item -Path { $_.BaseName } -Name ($_.FullName+".xxx") -Type File -Force
}
This errors out with
A positional parameter cannot be found that accepts argument "+xxx".
You need to wrap both the second Get-ChildItem (ls) and the New-Item in ForEach-Object statements. Also, do not pass $_.Basename as the path to New-Item. Do it either like this:
New-Item -Path ($_.FullName + '.xxx') -Type File -Force
or like this:
New-Item -Path $_.Directory -Name ($_.Name + '.xxx') -Type File -Force
Modified code:
foreach ($drive in $drivesArray) {
Get-ChildItem $drive | Where-Object {
$_.FullName -notlike "${Env:WinDir}*" -and
$_.FullName -notlike "${Env:ProgramFiles}*"
} | ForEach-Object {
Get-ChildItem $_.FullName -Recurse -ErrorAction SilentlyContinue
} | Where-Object {
-not $_.PSIsContainer -and
$_.Extension -notmatch '^\.(xxx|exe|html)$'
} | ForEach-Object {
New-Item -Path ($_.FullName + '.xxx') -Type File -Force
}
}
I am working with a script to clear old files off our file server. We are using this line in the script to find all files older than a certain date:
$oldFiles = Get-ChildItem $oldPath -Recurse | Where-Object { $_.lastwritetime -le $oldDate }
My question is, how do I ignore a certain directory in the $oldPath? For instance, if we had the following:
root
dir1
dir 2
subdir 1
subdir 2
dir 3
subdir 1
dir 4
And we want to ignore dir 2 and all subdirectories when building the list
Final working script:
$oldPath = "\\server\share"
$newDrive = "I:"
$oldDate = Get-Date -Date 1/1/2012
$oldFiles = Get-ChildItem $oldPath -Recurse -File | Where-Object {($_.PSParentPath -notmatch '\\Ignore Directory') -and $_.lastwritetime -le $oldDate }
$oldDirs = Get-ChildItem $oldPath -Recurse | Where-Object {$_.PSIsContainer -and ($_.PSParentPath -notmatch '\\Ignore Directory')} | select-object FullName
$oldDirs = $oldDirs | select -Unique
foreach ($oldDir in $oldDirs) {
$strdir = $newDrive + "\" + ($oldDir | Split-Path -NoQualifier | Out-String).trim().trim("\")
if (!(Test-Path $strdir)) {
Write-Host "$strdir does not exist. Creating directory..."
mkdir $strdir | Out-Null
} # end if
} # end foreach
foreach ($file in $oldFiles) {
$strfile = $newDrive + "\" + ($file.FullName | Split-Path -NoQualifier | Out-String).trim().trim("\")
Write-Host "Moving $file.FullName to $strfile..."
Move-Item $file.FullName -Destination $strfile -Force -WhatIf
} # end foreach
$oldfiles | select pspath | Split-Path -NoQualifier | Out-File "\\nelson\network share\ArchivedFiles.txt"
Modify your Where-Object condition to:
... | Where-Object {($_.PSParentPath -notmatch '\\dir 2') -and ($_.lastWriteTime -le $oldDate)}
Also, you probably want to filter out directory items as well so that $oldFiles contains only files e.g.:
$oldFiles = Get-ChildItem $oldPath -Recurse | Where {!$_.PSIsContainer -and ($_.PSParentPath -notmatch '\\dir 2') -and ($_.lastWriteTime -le $oldDate)}
If you're on PowerShell v3 you can use a new parameter on Get-ChildItem to simplify this to:
$oldFiles = Get-ChildItem $oldPath -Recurse -File | Where {($_.PSParentPath -notmatch '\\dir 2') -and ($_.lastWriteTime -le $oldDate)}
Something like this should work:
$exclude = Join-Path $oldPath 'dir 2'
$oldFiles = Get-ChildItem $oldPath -Recurse | ? {
-not $_.PSIsContainer -and
$_.FullName -notlike "$exclude\*" -and
$_.LastWriteTime -le $oldDate
}
Try $oldFiles = Get-ChildItem $oldPath -Recurse -Exclude "dir 2" | Where-Object { $_.lastwritetime -le $oldDate}