I'm trying to rename files that match values in column one of a csv adding the value in column 3 to the beginning of the file name leaving the rest of the file name intact. Here is what I have so far. I cant seem to figure out the Rename-Item.
# Common Paths
$PathRoot = "C:\Temp\somefiles" #Where the files are to rename
# Get csv file
$ClientAccounts = Import-CSV -path "\\server\some\path\to\csv\file.csv"
# Get each file and rename it
ForEach($row in $ClientAccounts)
{
$CurrentClientTaxId = $row[-1].TaxId
$CurrentClientName = $row[-1].ClientName
#loop through files
$FileExists = Test-Path -Path "$PathTotal\*$CurrentClientLB_Number*" #See if there is a file.
If ($FileExists -eq $true) #The file does exist.
{
#ReName File
Rename-Item -Path $PathRoot -NewName {$CurrentClientName + " " + $_.name}
}
}
Lets suppose your CSV file looks similar to this:
"LB_Number","TaxId","ClientName"
"987654","12345","Microsoft"
"321456","91234","Apple"
"741852","81234","HP"
Column 1 has the portion of the existing file name to match
Column 3 has the client name you want to prepend to the file name
Then your function could be something like this:
# Common Paths
$PathRoot = "C:\Temp\somefiles" # Where the files are to rename
# Get csv file
$ClientAccounts = Import-CSV -path "\\server\some\path\to\csv\file.csv"
# Loop through all clients in the CSV
foreach($client in $ClientAccounts) {
$CurrentClientLB_Number = $client.LB_Number
$CurrentClientTaxId = $client.TaxId # unused...??
$CurrentClientName = $client.ClientName
# get the file(s) using wildcards (there can be more than one)
# and rename them
Get-ChildItem -Path "$PathRoot\*$CurrentClientLB_Number*" -File | ForEach-Object {
$_ | Rename-Item -NewName ($CurrentClientName + " " + $_.Name)
}
# Curly braces work also, although this is not very common practice:
# Get-ChildItem -Path "$PathRoot\*$CurrentClientLB_Number*" -File |
# Rename-Item -NewName { ($CurrentClientName + " " + $_.Name) }
}
I use the -File parameter with Get-ChildItem so the function will only return files; not directories. If you are using PowerShell version 2.0, you need to replace that with | Where-Object { !$_.PSIsContainer }.
Related
is there a way to bulk rename items such that a folder with the items arranged in order would have their name changed into numbers with zero padding regardless of extension?
for example, a folder with files named:
file1.jpg
file2.jpg
file3.jpg
file4.png
file5.png
file6.png
file7.png
file8.jpg
file9.jpg
file10.mp4
would end up like this:
01.jpg
02.jpg
03.jpg
04.png
05.png
06.png
07.png
08.jpg
09.jpg
10.mp4
i had a script i found somewhere that can rename files in alphabetical order. however, it seems to only accepts conventionally bulk renamed files (done by selecting all the files, and renaming them such that they read "file (1).jpg" etc), which messes up the ordering when dealing with differing file extensions. it also doesn't seem to rename files with variations in their file names. here is what the code looked like:
Get-ChildItem -Path C:\Directory -Filter file* | % {
$matched = $_.BaseName -match "\((?<number>\d+)\)"
if (-not $matched) {break;}
[int]$number = $Matches["number"]
Rename-Item -Path $_.FullName -NewName "$($number.ToString("000"))$($_.Extension)"
}
If your intent is to rename the files based on the ending digits of their BaseName you can use Get-ChildItem in combination with Where-Object for filtering them and then pipe this result to Rename-Item using a delay-bind script block.
Needles to say, this code does not handle file collision. If there is more than one file with the same ending digits and the same extension this will error out.
Get-ChildItem -Filter file* | Where-Object { $_.BaseName -match '\d+$' } |
Rename-Item -NewName {
$basename = '{0:00}' -f [int][regex]::Match($_.BaseName, '\d+$').Value
$basename + $_.Extension
}
To test the code you can use the following:
#'
file1.jpg
file2.jpg
file3.jpg
file4.png
file5.png
file6.png
file7.png
file8.jpg
file9.jpg
file10.mp4
'# -split '\r?\n' -as [System.IO.FileInfo[]] | ForEach-Object {
$basename = '{0:00}' -f [int][regex]::Match($_.BaseName, '\d+$').Value
$basename + $_.Extension
}
You could just use the number of files found in the folder to create the appropriate 'numbering' format for renaming them.
$files = (Get-ChildItem -Path 'D:\Test' -File) | Sort-Object Name
# depending on the number of files, create a formating template
# to get the number of leading zeros correct.
# example: 645 files would create this format: '{0:000}{1}'
$format = '{0:' + '0' * ($files.Count).ToString().Length + '}{1}'
# a counter for the index number
$index = 1
# now loop over the files and rename them
foreach ($file in $files) {
$file | Rename-Item -NewName ($format -f $index++, $file.Extension) -WhatIf
}
The -WhatIf switch is a safety measure. With this, no file gets actually renamed, you will only see in the console what WOULD happen. Once you are content with that, remove the -WhatIf switch from the code and run again to rename all your files in the folder
I am looking to batch rename part of a pdf file using a csv file. I have a csv file with two columns, name and Newname. My pdf files have a naming convention of 222222_test (for example) and are located in the C:\TEST folder. In the csv file, 222222 is in the name column and Jonathan is in the Newname column.
The folder is really going to have hundreds of pdf documents whenever I can get this to work.
$csv = Import-Csv "C:\TEST\Book1.csv"
# location of your files
$files = get-childitem "C:\TEST\*.DOCX"
foreach($item in $CSV){
foreach($file in $files){
if($item.name -eq $file.basename){
rename-item $file.fullname -NewName "$($item.newname)$($file.extension)" -Verbose
}
}
}
I am looking for a way for the 222222 (only) to be changed to Jonathan so the pdf file would be Jonathan_test. I was able to use the code when the file name is only 222222 but when the pdf is 222222_test, the code is not working.
Give this a try, remove the WhatIf if it works for your files. Else, we'll need to see some sample data from the csv.
foreach ($item in $CSV) {
foreach ($file in $files) {
if ($item.name -eq $file.basename) {
Rename-Item $file.fullname -NewName $($file.FullName -replace $item.name, $item.newname) -WhatIf
}
}
}
With hundreds of CSV rows, it pays to build up a hashtable up front that maps old names to new names.
You then only need to loop once over the file names, performing a fast hashtable lookup in each iteration.
# Initialize the hashtable.
$ht = #{}
# Fill the hashtable, with the "name" column's values as the keys,
# and the "newname" columns as the values.
Import-Csv C:\TEST\Book1.csv |
ForEach-Object {
$ht.Add($_.name, $_.newname)
}
# Loop over the files and rename them based on the hashtable
Get-ChildItem C:\TEST\*.DOCX | Rename-Item -NewName {
$prefix = ($_.BaseName -split '_')[0] # Get prefix (before "_")
$newPrefix = $ht[$prefix] # Look up the prefix in the hashtable.
if ($newPrefix) { # Replace the prefix, if a match was found.
$newPrefix + $_.Name.Substring($prefix.Length)
}
else { # No replacement - output the original name, which is a no-op.
$_.Name
}
} -WhatIf
-WhatIf previews the renaming operations; remove it to perform actual renaming.
For over 800 files I need information that's in the file name to be included in the contents of the text file (actually .md files).
The file names are always of the same structure, something like 0000-title-text-1-23.md; only the 1-23 part changes (and that is the information I need).
I am a novice as it comes to scripting, but I figured out that this should be an easy task for PowerShell — yet I don't get it working the way I want. What did come closest:
Get-Childitem "C:\PATH\*.md" | ForEach-Object{
$fileName = $_.BaseName
Add-Content -Path .\*.md -Value $fileName
}
But that adds all file names in the directory, not just the one from the file itself.
What am I doing wrong?
Use this code to do what you exactly want,
it will get the last 2 parts of your filename and
put it in the beginning of your file content.
Get-Childitem "C:\PATH\*.md" | ForEach-Object{
$fileNameParts = ($_.BaseName).split('-')
$info = $fileNameParts[-2] + '-' + $fileNameParts[-1]
$info + (Get-Content $_ -Raw) | Set-Content $_
}
Something like this would work although it does add the content to the end of the file:
#Get all the .txt or .md files in your location
Get-ChildItem -Filter "*.txt" | Foreach-Object{
#Get the base name of the file
$baseName = $_.BaseName
#Split the base name
$array = $baseName -Split '-'
#Put the third and fourth element in the array into a separate variable
#This will be added to the file
$addToFile = $array[3] + '-' + $array[4]
#Add the $addToFile variable to the file
Add-Content $_.FullName -Value $addToFile
}
I have many folders and inside these different files. Each folder and their children files have the same name and different extension, so in the ABC folder there are the ABC.png, ABC.prj, ABC.pgw files, in the DEF folder there are the DEF.png, DEF.prj, DEF.pgw files and so on.
With a script I have created a txt file with the list of png file names. Then I put in row 2 a new name for the name in row1, in row 4 a new name for the name in row 3, and so on.
Now I'm searching a powershell script that:
- scan all folder for the name in row 1 and replace it with name in row2
- scan all folder for the name in row 3 and replace it with name in row4 and so on
I have try with this below, but it doesn't work.
Have you some suggestions? Thank you
$0=0
$1=1
do {
$find=Get-Content C:\1\Srv\MapsName.txt | Select -Index $0
$repl=Get-Content C:\1\Srv\MapsName.txt | Select -Index $1
Get-ChildItem C:\1\newmaps -Recurse | Rename-Item -NewName { $_.name -replace $find, $repl} -verbose
$0=$0+2
$1=$1+2
}
until ($0 -eq "")
I believe there are several things wrong with your code and also the code Manuel gave you.
Although you have a list of old filenames and new filenames, you are not using that in the Get-ChildItem cmdlet, but instead try and replace all files it finds.
Using -replace uses a Regular Expression replace, that means the special character . inside the filename is regarded as Any Character, not simply a dot.
You are trying to find *.png files, but you do not add a -Filter with the Get-ChildItem cmdlet, so now it will return all filetypes.
Anyway, I have a different approach for you:
If your input file C:\1\Srv\MapsName.txt looks anything like this:
picture1.png
ABC_1.png
picture2.png
DEF_1.png
picture3.png
DEF_2.png
The following code will use that to build a lookup Hashtable so it can act on the files mentioned in the input file and leave all others unchanged.
$mapsFile = 'C:\1\Srv\2_MapsName.txt'
$searchPath = 'C:\1\NewMaps'
# Read the input file as an array of strings.
# Every even index contains the file name to search for.
# Every odd index number has the new name for that file.
$lines = Get-Content $mapsFile
# Create a hashtable to store the filename to find
# as Key, and the replacement name as Value
$lookup = #{}
for ($index = 0; $index -lt $lines.Count -1; $index += 2) {
$lookup[$lines[$index]] = $lines[$index + 1]
}
# Next, get a collection of FileInfo objects of *.png files
# If you need to get multiple extensions, remove the -Filter and add -Include '*.png','*.jpg' etc.
$files = Get-ChildItem -Path $searchPath -Filter '*.png' -File -Recurse
foreach ($file in $files) {
# If the file name can be found as Key in the $lookup Hashtable
$find = $file.Name
if ($lookup.ContainsKey($find)) {
# Rename the file with the replacement name in the Value of the lookup table
Write-Host "Renaming '$($file.FullName)' --> $($lookup[$find])"
$file | Rename-Item -NewName $lookup[$find]
}
}
Edit
If the input text file 'C:\1\Srv\MapsName.txt' does NOT contain filenames including their extension, change the final foreach loop into this:
foreach ($file in $files) {
# If the file name can be found as Key in the $lookup Hashtable
# Look for the file name without extension as it is not given in the 'MapsName.txt' file.
$find = [System.IO.Path]::GetFileNameWithoutExtension($file.Name)
if ($lookup.ContainsKey($find)) {
# Rename the file with the replacement name in the Value of the lookup table
# Make sure to add the file's extension if any.
$newName = $lookup[$find] + $file.Extension
Write-Host "Renaming '$($file.FullName)' --> '$newName'"
$file | Rename-Item -NewName $newName
}
}
Hope that helps
The problem in your snippet is that it never ends.
I tried it and it works but keeps looping forever.
I created a folder with the files a.txt, b.txt and c.txt.
And in the map.txt I have this content:
a.txt
a2.md
b.txt
b2.md
c.txt
c2.md
Running the following script I managed to rename every file to be as expected.
$0=0
$1=1
$find=Get-Content D:\map.txt | Select -Index $0
while($find) {
$find=Get-Content D:\map.txt | Select -Index $0
$repl=Get-Content D:\map.txt | Select -Index $1
if(!$find -Or !$repl) {
break;
}
Get-ChildItem D:\Files -Recurse | Rename-Item -NewName { $_.name -replace $find, $repl} -verbose
$0=$0+2
$1=$1+2
}
Currently I am using PS to copy files from a network location based on a CSV file, then I am renaming them using a variation of the same data. This requires that I run two separate commands.
How do I consolidate these commands into one?
Copy:
import-csv C:\TEST\test.csv | foreach {copy-item -path $_.npath -destination 'C:\TEST\'}
Paste:
import-csv C:\TEST\test.csv | foreach {rename-item -path $_.lpath -newname $_.newalias}
Notice that the -path trigger in each case refers to a separate variable header, npath vs. lpath which correspond to the network file location, and then a local file location which have been manually entered.
On the same note, how could I concatenate this variable to constant data. If I have a variable fn which represents the file name and another path, could I theoretically do:
foreach {rename-item -path 'C:\TEST\' + $_.fn
Or:
foreach {rename-item -path $_.path + $_.fn
Just append the two commands
import-csv C:\TEST\test.csv | foreach {copy-item -path $_.npath -destination 'C:\TEST\';rename-item -path $_.lpath -newname $_.newalias }
for your second question there are lots of ways to append string
C:(...)WindowsPowerShell>$data = "bob"
C:(...)WindowsPowerShell>echo "this is a $data"
C:(...)WindowsPowerShell>$concat = "hi" + " george"
C:(...)WindowsPowerShell>$concat
hi george
C:(...)WindowsPowerShell>[string]::Format("{0} {1}","string 1","string 2")
string 1 string 2