Rename files with same name and extension (usually .pdf) in the sub directories with incrementing number - powershell-2.0

My folder structure is like this:
Folder 1
File 1.pdf
File 2.pdf
File 3.pdf
Folder 2
File 1.pdf ---> Rename this to "File 1-1.pdf"
File 2.pdf ---> Rename this to "File 2-1.pdf"
File 5.pdf
SubFolder 2.1
File 2.pdf ---> Rename this to "File 2-2.pdf"
File 4.pdf
File 5.pdf ---> Rename this to "File 5-1.pdf"
Folder 3
File 1.pdf ---> Rename this to "File 1-2.pdf"
File 2.pdf ---> Rename this to "File 2-3.pdf"
File 4.pdf ---> Rename this to "File 4-1.pdf"
File 6.pdf
File 7.pdf
This is what i was trying, but its certainly wrong. I am using Powershell Version 2.0.
$i = 1
$folder = Get-ChildItem "C:\Users\xyz\testfolder\pdf_files\"
foreach ($file in $folder){
if (($file.BaseName + $file.extension) -match ($file.BaseName +
$file.extension)){
Rename-Item -Path $file.FullName -NewName "$($file.BaseName +
$i)$($file.extension)" -Verbose -WhatIf
$i++
}
}

I just found the answer to my question through a very kind person. Anyway..here is the PS script that worked for me..
$Files = Get-ChildItem -LiteralPath 'C:\Foo\Bar\Files\' -Recurse | Where-
Object { -not $_.PSIsContainer }
$FileNameCounts = #{}
ForEach ($File In $Files) {
$BaseName = $File.BaseName
If ($FileNameCounts.Keys -contains $BaseName) {
$NewName = '{0}-{1}{2}' -f $BaseName, $FileNameCounts[$BaseName],
$File.Extension
$File | Rename-Item -NewName $NewName -WhatIf
$FileNameCounts[$BaseName]++
} Else {
$FileNameCounts[$BaseName] = 1
}
}

Here is an alternative way of doing the same thing.
Get-ChildItem -Recurse -file | Group-Object Name | foreach-object {
$script:i = 1
$_.Group | Select-Object -Skip 1 |
Rename-Item -newname { "{0}-{1}{2}" -f $_.basename, $script:i++, $_.extension }
}
Group-Object gives us all the files with the same name, and for each group of names, the first file is skipped and the rest is being renamed.
It also takes advantage of a feature that was designed specifically for this usecase, that -NewName can take a scriptblock as a parameter. The scriptblock will be invoked for each pipeline item and the result will be passed to Rename-Item as the newname.

Related

Powershell: batch rename files, add counter in front (with leading 0)

I am trying (as so many others) to batch rename files in a folder, by adding a counter to the front of the filename with leading zeros.
Here is what I have:
b.txt
c.txt
...
zzz.txt
Here is what I want:
001_b.txt
002_c.txt
...
893_zzz.txt
My code so far:
$originalFiles = Get-ChildItem "C:\Users\abc\" -Filter *.txt
$i = 1
ForEach ($originalFile in $originalFiles) {
Rename-Item -Path $originalFile.FullName -NewName (($originalFile.Directory.FullName) + "\" + $i + $originalFile.Name)
$i++
}
I am missing the underscore between the the number and the filename. And I am missing the leading zeros.
Any suggestions are welcome.
Sorry for this basic question, this is my first PowerShell experience.
This should do the trick:
$MyPath = "C:\Users\abc\"
$i=1
Get-ChildItem -Path $MyPath -Filter "*.txt" | Sort-Object | ForEach-Object {
Rename-Item -Path $_.FullName -NewName (Join-Path -Path $_.Directory.FullName -ChildPath "$('{0:d3}' -f $i)_$($_.Name)")
$i++
}
Keep in mind that if you will have 1000 or more files, you will need more than 3 digits

powershell v2 - rename a file whose name matches a string containing a wildcard

I'm trying to create a script to rename a file that matches a given filename, with a wildcard character. e.g.
I have a folder containing the following files:
201412180900_filename_123.log
201412181000_filename_123.log
201412181100_filename_456.log
filename_789.log
I want to scan through this folder, and append the current time to the start of any file starting with the word 'filename'
I have the following so far:
$d = Get-Date -format "yyyyMMddHHmm"
$dir = "C:\test"
$file = "filename*.log"
get-childitem -Path "$dir" | where-object { $_.Name -like "$file" } | rename-item -path $_ -newname $d."_".$_.name
but it doesn't work.
As I see it the individual sections 'should' work from my reading of the documentation, but clearly something is wrong. If someone can point it out it would be appreciated.
We're getting closer. It would appear that -path in the rename-item section needs to be $dir$_ as $_ (seemingly) only contains the filename. (The get-help example suggests it needs to be the full path and filename)
If I take out the rename-item section and replace it with %{write-host $d"_"$_} it gives the correct new filename
However, simply placing this into rename-item section still doesn't update the filename.
rename-item -path $dir$_ -newname $d"_"$_
SUCCESS
the correct syntax appears to be:
get-childitem -Path "$dir" | where-object { $_.Name -like "$file" } | %{rename-item -path $dir$_ -newname $d"_"$_}
The missing element was the %{ ... } surrounding the rename-item section which enabled me to reference $_
$d = Get-Date -format "yyyyMMddHHmm"
$dir = "C:\test"
$file = "filename*.log"
get-childitem -Path $dir | where-object { $_.Name -like $file } | %{ rename-item -LiteralPath $_.FullName -NewName "$d`_$($_.name)" }
This should work, assuming that the errors were relating to "Cannot bind argument to parameter 'Path'", and the NewName string.
Issues included:
Not being able to reference $_ when not in a foreach style block (see here)
The Concatenation issue raised by #alroc
You could, instead of passing the pipeline object to a Foreach-Object, pass directly to the Rename-Item - but I'm unsure quite how to reference the name of the object for the -NewName parameter.
I don't recall . being a string concatenation operator in PowerShell (I may be wrong). Try this:
rename-item -path $_ -newname "$d_$($_.name)"
Or this
rename-item -path $_ -newname ($d + "_" + $_.name)
Or even this
rename-item -path $_ -newname ({0}_{1} -f $d,$_.name)
See the answers here
Yet another way to do it without using foreach or %, but a script block instead:
get-childitem -Path "$dir" -filter $file | rename-item -newname { $d + "_" + $_.name }
See the examples in the doc for rename-item: https://msdn.microsoft.com/en-us/powershell/reference/3.0/microsoft.powershell.management/rename-item There's a -replace operator too, but it can't use wildcards? Oh, you don't need wildcards:
get-childitem -Path "$dir" -filter $file | rename-item -newname { $_.name -replace 'filename',($d + '_filename') }

Powershell wildcard on returns items

I'm trying to determine whether the directories under a specified root directory contain files that match a certain pattern, in my case RT*.dcm.
I'm using Powershell 2.0 and I first obtain all sub-directories beneath the specified root directory using
$dirList = Get-ChildItem $homeDir -recurse | where {$_.Attributes -eq 'Directory'} | Select-Object FullName
I then loop through these to see if they contain *.dcm files using (perhaps there's a better way?)
# Find files with a "dcm" extension.
$fileList = Get-ChildItem $dir.fullname | where {$_.extension -eq ".dcm"} | Select-Object FullName
# Look for directories that contain *.dcm files
if ($fileList.Count -gt 0) {
[Console]::WriteLine("Dicom directory: " + $dir.fullname)
$dicomDirList += $dir
}
The above two sections work ok
I then search through the found directories using
foreach($dir in $dicomDirList) {
$rtFileList = Get-ChildItem $dir | where {$_.name -like "RT*.dcm"} | Select-Object FullName
foreach($file in $rtFileList) {
[Console]::WriteLine("RT likey file: " + $file.fullname)
}
}
However this doesn't find the files I know that are there?
If I use
Get-ChildItem C:\myfolder\RT*.dcm
this works, but I can't figure out how to use the returned items from the previous Get-ChildItem call
Could someone please point me in the right direction?
It looks like you may be over-complicating things.
To accomplish what Get-ChildItem C:\myfolder\RT*.dcm does for the entirety of $homeDir (which is what I believe you're trying to do), you can use a single Get-ChildItem command:
Get-ChildItem $homeDir -Recurse | Where-Object{$_.Name -like "RT*.dcm"}
This searches the entirety of $homeDir recursively for all of the .dcm files you're looking for and returns them.

PowerShell copy random files

I am trying to copy 7 random .txt files to a different location, but sub-folders get copied instead of the .txt files.
Here is my script:
$d = #(gci G:\Users\Public\Test) | resolve-path | get-random -count 2
$d | gci | get-random -count 7
Copy-Item $d -destination G:\Users\Public\Videos
What do I need to change?
One possible solution might be to use the PSIsContainer attribute to filter out folders.
I tried the following...
$d = gci "C:\Work\a\*.txt" | Where {$_.psIsContainer -eq $false}| resolve-path | get-random -count 7
Copy-Item $d -destination C:\Work\b
The where clause filtered out anything that was not a "container" and ignored the test folders I had set up. If you need .txt files specifically then use the wildcard included in the path as above.
Also, if you were to add -recurse then it would presumably search in all sub folders of your original search location and still filter out any "folders" for copying. Though I haven't tested this very thoroughly.
$d = gci "C:\Work\a\*.txt" -recurse | Where {$_.psIsContainer -eq $false}| resolve-path | get-random -count 7

What is wrong with this script to create files in all subfolders?

I have a directory tree, consisting of several layers, within which I want to create 30 placeholder files, recursively in each folder.
The tree looks something like this:
--F:\inbox\test
----folder1
------subfolder1
------subfolder2
----folder2
------subfolder1
------subfolder2
----folder3
------subfolder1
------subfolder2
Here is what I have.
$folders = gci -path f:\inbox\test -recurse | where {$_.PsIsContainer}
foreach ($folder in $folders) {
1..30 | % { New-Item -Name "$_.txt" -Value (get-date).tostring() -Itemtype file -force}
}
This just creates 30 files in the root folder. I know I am missing something in my logic.
You're not telling new-item where to put the file, so it uses the current working directory. Fortunately, this is easliy fixed with the -Path parameter for the cmdlet.
$folders = gci -path f:\inbox\test -recurse | where {$_.PsIsContainer}
foreach ($folder in $folders) {
1..30 | % { New-Item -Path $folder.FullName -Name "$_.txt" -Value (get-date).tostring() -Itemtype file -force}
}

Resources