Powershell - find file by 'file name' and rename based on CSV - csv

I have a set of files (OldName) in a Windows directory that I would like to rename (NewName) based on the following CSV file:
OldName,NewName
Sources/texas play_PGC_Cpgc_entryPoint_Cbp_1.f4v,01 Texas Play.f4v
Sources/texas play_PGC_Cpgc_entryPoint_Dbp_1.f4v,02 First Song.f4v
Sources/texas play_PGC_Cpgc_entryPoint_Ebp_1.f4v,03 Yellow Rose.f4v
I'm not sure how to loop thru the CSV file... finding each file and replacing.
Any thoughts would be appreciated.

First Import Your CSV file into powershell
$AnyVariableName = Import-Csv "$env:USERPROFILE:\Desktop\directoryname.txt"
Note: In my example, the path to the CSV file is on my desktop, but it may be different in yours.
Then use a foreach loop rename the items
foreach ($objName in $AnyVariableName){
Rename-Item $objName.OldName $objName.NewName
}

One way to do it is to create two lists and loop though each of them. The CSV file will be a reference list, so we'll grab the contents and convert it from CSV then store it in a variable
$CSVRef = Get-Content "C:\Path\To\CSV.csv" | ConvertFrom-CSV
Then we'll get the list of files who's names you want to change, and loop through each file. From inside the loop you can run another loop to find the current name in your reference list, and then change it to the new name.
Get-ChildItem "C:\path\to\f4v\files" -Filter *.f4v | ForEach-Object {
#Grab the current item in a variable to access it within the second loop
$CurrentFile = $_
$CSVRef | ForEach-Object {
if ($CurrentFile.Name -ilike $_.OldName) {
Rename-Item $CurrentFile.FullPath $_.NewName
}
}
}
So during the second loop we try to compare the file name with every "OldName" item in the CSV file list. If the OldName matches somewhere in the current file we're looping through then we run Rename-Item and provide it the NewName. It should automatically rename the file.

Combining both examples works great
$CSVRef = Import-Csv "C:\Temp\Filename.txt"
Get-ChildItem "C:\Temp\FileFolder" -Filter *.pdf | ForEach-Object {
$CurrentFile = $_
ForEach ($objName in $CSVRef) {
if ($CurrentFile.Name -ilike $objName.OLDNAME) {
Rename-Item $CurrentFile.FullName $objName.NEWNAME
}
}
}

Related

Merge two CSV files into one

I have two CSV files that don't have any unique identifiers. The number of rows of both files is always the same. I want to merge them as it is (the 2nd CSV as additional columns into the 1st CSV).
Content of file1.csv:
Server,Info
server1,item1
server1,item2
server1,item3
server2,item1
server2,item2
server2,item3
server3,item1
server3,item2
server3,item3
Content of file2.csv:
Items,ColumnA,ColumnB,ColumnC
item#1:,aa,jj,ss
item#2:,bb,kk,tt
item#3:,cc,ll,uu
item#1:,dd,mm,vv
item#2:,ee,nn,ww
item#3:,ff,oo,xx
item#1:,gg,pp,yy
item#2:,hh,qq,zz
item#3:,ii,rr,ab
Expecting output of csv file:
Server,Info,Items,ColumnA,ColumnB,ColumnC
server1,item1,item#1:,aa,jj,ss
server1,item2,item#2:,bb,kk,tt
server1,item3,item#3:,cc,ll,uu
server2,item1,item#1:,dd,mm,vv
server2,item2,item#2:,ee,nn,ww
server2,item3,item#3:,ff,oo,xx
server3,item1,item#1:,gg,pp,yy
server3,item2,item#2:,hh,qq,zz
server3,item3,item#3:,ii,rr,ab
I have search intensively in the net, but couldn't find any solution... Would really appreciate if someone can provide me some answers here...
You can use the Get-Content cmdlet to read both CSV files, iterate over it using a for-loop and use a format string to join them together. Finally, write the new file back using the Out-File cmdlet:
$file1 = Get-Content 'Path_to_file1.csv'
$file2 = Get-Content 'Path_to_file2.csv'
$content = for ($i = 0; $i -lt $file1.Length; $i++)
{
'{0},{1}' -f $file1[$i].Trim(), $file2[$i].Trim()
}
$content | out-file 'Path_to_file3.csv'

Rename Files & Folders Keywords - Using a CSV Look Up File

I would like to rename files and folders based on keywords found in a CSV file.
The CSV holds the search and replace keywords that will make up file and folder names.
Search | Replace
Document | DTX
Processing | PRX
Implementation | IMX
...
Not all the file names include each word in the file name.
Not all the folders will include each word in the folder name
Powershell will have to search the child item ie the folder and file
names.
If it finds the word (match) - Substitute from the CSV
I have looked at these threads to help me:
Using Powershell to recursively rename directories using a lookup file
powershell script to rename all files in directory
http://code.adonline.id.au/batch-rename-files/
I have only managed below snippet
$folder = "C:\Folders" #target folder containing files
$csv = "C:\FileNameKeywords.csv" #path to CSV file
cd ($folder);
Import-Csv ($csv) | foreach {
Rename-Item -Path $_.Path -NewName $_.Filename
}
It only replaces one at a time.
Question:
How can I recursively search and replace in file and Folder Names using a CSV as a look up or reference file.
When you have the need to look up values by other values the usual go-to data structure is a dictionary, or in PowerShell terms a hashtable. Read your CSV into a dictionary like this:
$keywords = #{}
Import-Csv $csv | ForEach-Object {
$keywords[$_.Search] = $_.Replace
}
Then traverse your folder tree and build the new filenames by replacing each key with its respective value:
Get-ChildItem $folder -Recurse | ForEach-Object {
$newname = $_.Name
foreach ($word in $keywords.Keys) {
$newname = $newname.Replace($word, $keywords[$word])
}
if ($_.Name -ne $newname) {
Rename-Item -Path $_.FullName -NewName $newname
}
}
ill give it a shot. I'm assuming search and replace are your headers in this scenario. this in addition to your $folder and $csv variables.
$csvobject=import-csv $csv
Foreach($obj in $csvobject){
$search=$obj.search
$replace=$obj.replace
get-childitem path $folder |where{$_.name -like "$($obj.search)"} | rename-item -newname {$_.name -replace "$search", "$replace"} }
The replace handles regex so u will need to make sure Any special characters are properly escaped.

Powershell Copy Directories - Using A CSV Listing - Source - Destination Path

I am trying to Copy Directories - folders and Sub folders to another location using a CSV file that lists the source and destination of each directory or folder to be copied.
The Contents of the CSV are as such below:
I have referenced this thread:
https://serverfault.com/questions/399325/copying-list-of-files-through-powershell
Import-CSV C:\Users\WP\Desktop\a.csv | foreach{Copy-item "$_.Source" "$_.Destination"}
Error Received
CategoryInfo : ObjectNotFound: (#{Source=C:String) [Copy-Item], DriveNotFoundException
+ FullyQualifiedErrorId : DriveNotFound,Microsoft.PowerShell.Commands.CopyItemCommand
The other question I have is if in the CSV I want to copy to a folder that does not exists in the destination - can I use the CSV to command powershell to create the folder?
Thank you for your advice.
PowerShell will not expand the variable and access the property of the object inside the variable if you have them placed in double quotes by default. Only the '$_' is being expanded and '.source' is being tacked on to the end of the string, so from the view of the shell, your command looks something like Copy-item "{source=C:\Users\WP\Desktop\a;Destination=C:\Users\WP\Desktop\a}.Source" "{source=C:\Users\WP\Desktop\a;Destination=C:\Users\WP\Desktop\a}.Destination", which is probably not what you mean.
Here is the syntax that should work (I also included -Recurse so that it will copy the items inside the directory as well)
Import-CSV C:\Users\WP\Desktop\a.csv | foreach{Copy-item -Path $_.Source -Destination $_.Destination -Recurse}
Note: if you want to access the properties on an object inside of double quotes, use this syntax "$($_.source)".
For a csv like this:
Source,Destination
D:\junk\test1,D:\junk\test3
D:\junk\test2,D:\junk\test4
You can use code like the following:
$csv = Import-Csv D:\junk\test.csv
$csv | ForEach-Object {
if (-not (Test-Path $_.Destination)) {
New-Item -Name $_.Destination -ItemType Directory -Force -WhatIf
}
Copy-Item $_.Source $_.Destination -Recurse -Force -WhatIf
}
Suggestions for learning more about PowerShell:
Use WhatIf to test things.
Research what each line of this code does.
Experiment with code to see what it does.
Learn and use the debugger (PowerShell ISE) to help you write better code.
Remove the WhatIf parameters from the code to have it execute for real...
If you have dozens of problems that all involve doing the same thing with each element of a list, you might want to consider getting or writing a generic CSV template expander tool, like Expand-csv. With this tool you start with a CSV file and a template, and generate a script that contains all the commands.
Sample.csv looks like this:
Source,Destination
C:\Users\WP\Desktop\a,C:\Users\WP\Desktop\c
C:\Users\WP\Desktop\b,C:\Users\WP\Desktop\d
Sample.tmplt looks like this:
Copy-Item -Path $Source -Destination $Destination -Recurse
And the command to invoke Expand-csv looks like this:
Expand-csv Sample.csv Sample.tmplt > Sample.ps1
The output file, Sample.ps1 contains one copy command for each entry in the CSV file
And here is the definition of Expand-csv:
<# This function is a table driven template tool.
It's a refinement of an earlier attempt.
It generates output from a template and
a driver table. The template file contains plain
text and embedded variables. The driver table
(in a csv file) has one column for each variable,
and one row for each expansion to be generated.
5/13/2015
#>
function Expand-csv {
[CmdletBinding()]
Param(
[Parameter(Mandatory=$true)]
[string] $driver,
[Parameter(Mandatory=$true)]
[string] $template
)
Process
{
$OFS = "`r`n"
$list = Import-Csv $driver
[string]$pattern = Get-Content $template
foreach ($item in $list) {
foreach ($key in $item.psobject.properties) {
Set-variable -name $key.name -value $key.value
}
$ExecutionContext.InvokeCommand.ExpandString($pattern)
}
}
}

How do I access the name field from my CSV

My CSV looks like
User,Location
Louis.Smith,CH
I can use a Cmd-Let to show me users from the file
Import-Csv "decommisioned_users.csv"| Where-Object{$_.Location -match "CH"}
I want to move folders to another folder
So in the above example,
We would create a String that looks like
F:\Users\louis.smith
and move that folder to
F:\ArchivedUsers\louis.smith
I can't seem to figure out how to get "louis.smith" from my CSV file (1st column)
Use ForEach-Object. Example:
Import-Csv "decommissioned_users.csv" | Where-Object { $_.Location -eq "CH" } | ForEach-Object {
Move-Object F:\Users\$($_.User) F:\ArchivedUsers\$($_.User) -WhatIf
}
Of course, remove -WhatIf to actually run the command.

Powershell: Reading in a column from a .CSV file and then adding a specific string to the beginning and exporting again

I'm attempting to write a script which will read in a CSV generated by querying AD for user information (that part is done) but then will allow me to add a string to the beginning of each value of a column in the CSV file and then export it.
For instance we have this CSV file:
"displayname","Office"
Bob,7142
Janet,8923
SantaClaus,0912
NicCage,0823
I want to take each entry for "Office", add the string "BUG" before it and then export it back out. The modified CSV should look like:
"displayname","Office"
Bob,BUG7142
Janet,BUG8923
SantaClaus,BUG0912
NicCage,BUG0823
At this point, I've been attempting to read in just the "Office" column and then displaying it with "Write-Host". The idea being that if I can do that then maybe I can create a new variable that would be something like:
$BUG = "BUG"
$NewVar = $BUG$Office
Which would hopefully look like the second CSV file. I am extremely new to powershell scripting.
The attempts I've made so far are these:
Attempt #1:
$UserList = Import-CSV C:\Users\username\CSV.csv
$UserList | ForEach-Object ($_.Office) { $UserList }
Attempt #2:
$projectName = import-csv C:\Users\username\CSV.csv | % {$_.Office}
$BUG = "BUG"
$projectName | ForEach-Object ($_) {$projectName}
Attempt #3:
$UserList = Import-CSV C:\Users\username\CSV.csv
#ForEach ($Office in $Userlist) {
#Write-Host $UserList.Office
#}
Attempt #4:
Import-Csv "C:\Users\username\CSV.csv" -Header ("displayname","Office","whenCreated","EmailAddress") | Select-Object Office | Export-CSV -Path C:\users\Username\test.csv
I have gotten it to read out just the Office numbers before using the ForEach-Object loop structure but then it never stops reading out the office numbers so that's unhelpful.
I think I'm going in the right direction, but I just can't figure out how to modify a column like this.
Instead of trying to extract the Office column, just pipe the full data set (all columns) to ForEach-Object, change the value of the Office property and pipe it back to Export-Csv:
$Prefix = "BUG"
Import-Csv .\file.csv | ForEach-Object {
$_.Office = $Prefix + $_.Office
$_
} | Export-Csv .\file_modified.csv -NoTypeInformation