Update links in Excel files - excel

I am trying to modify some Excel files that link to other Excel files.
The files that are linked to are in sub-directories. I am going to move all the files to a root directory and then run a script to change the links.
I am able to find the links within each file but I am unable to modify them (see below)
Any ideas?
Thanks
P
#get all the excel files in the directory
Get-ChildItem $sourceDir -Filter *.xl* |
Foreach-Object {
write-host -ForegroundColor Yellow $_.FullName
$workbook = $excel.Workbooks.Open($_.FullName)
foreach ($link in $workbook.LinkSources(1))
{
write-host $link.Address
#this gives me .... C:\temp\files\childfile1.xlsx etc
# $link.Address seems to be read only?
#$link.Address = $newLink
#this doesn't seem to work either ...
#$workbook. .ChangeLink($link,$newlink,1)
}
#$workbook.Save()
$workbook.Close()

If you are modifying XLSX files, you can update the link without using Excel. Ultimately these are a zip archive with a different extension. If you create a copy of the file to have a ZIP extension, you can use Expand-Archive to access the various files and update those accordingly, then Compress-Archive to generate a new Excel file.
In the archive, look for /workbook.xml, which will identify sheets by name. /rels/workbook.xlm.rels can be used to translate from sheetId to the worksheet (Target), a file in /worksheets (e.g. "worksheets/sheets3.xml") and you can infer the relationship file, which will be in /workseets/_rels (e.g. /worksheets/_rels/sheet3.xml).
Using the worksheet you can find the associated hyperlink based upon cell reference, using the ref attribute, which gives you the r:id attribute. Us can us this value to lookup up the appropriate Relationship by Id. You would then need to update the Target appropriately.
Of course, if you know your original link, and it is unique (or you are altering them all the same way), you could do a search and replace across the .rels files.
Once you've saved you change, you just need to create the new file, which you can do using Compress-Archive. You'll need to do this to a file with a .Zip extension, then rename.
Here is an example based upon a XSLX with a link to Yahoo on the first sheet (note: the first 3 sheets normally have the XML and sheet names match, until altered. Don't count on that for production)
copy-item c:\temp\links.xlsx c:\temp\links.zip
expand-archive c:\temp\links.zip c:\temp\links_zip
$content = get-content c:\temp\links_zip\xl\worksheets\_rels\sheet1.xml.rels -raw # allow file to close
$content | %{ $_ -replace 'http://www.yahoo.com','http://www.google.com'} | set-content c:\temp\links_zip\xl\worksheets\_rels\sheet1.xml.rels
compress-archive c:\temp\links_zip\* c:\temp\links_alt.zip
remove-item c:\temp\links_zip -Recurse
rename-item c:\temp\links_alt.zip c:\temp\links_alt.xlsx
c:\temp\links_alt.xlsx

Related

Looking to validate that certain string is present in a text file, send warning if not

I have a process where files containing data are generated in separate locations, saved to a networked location, and merged into a single file.
And the end of the process, I would like to check that all locations are present in that merged file, and notify me if not.
I am having a problem finding a way to identify that a string specific to each location isn't present, to be used in an if statement, but it doesn't seem to be identifying the string correctly?
I have tried :
get-childitem -filter *daily.csv.ready \\x.x.x.x\data\* -recurse | where-object {$_ -notin 'D,KPI,KPI,1,'}
I know it's probably easier to do nothing if it is present, and perform the warning action if not, but I'm curious if this can be done in the reverse.
Thank you,
As Doug Maurer points out, your command does not search through the content of the files output by the Get-ChildItem command, because what that cmdlet emits are System.IO.FileInfo (or, potentially, System.IO.DirectoryInfo) instances containing metadata about the matching files (directories) rather than their content.
In other words: the automatic $_ variable in your Where-Object command refers to an object describing a file rather than its content.
However, you can pipe System.IO.FileInfo instances to the Select-String cmdlet, which indeed searches the input files' content:
Get-ChildItem -Filter *daily.csv.ready \\x.x.x.x\data\* -Recurse |
Where-Object { $_ | Select-String -Quiet -NotMatch 'D,KPI,KPI,1,' }

Delete rows in a .CSV file containing specific character with Powershell

I receive an automatic weekly export from a system in a .csv format. It contains a lot of usernames with the initials of the users (e.g. "fl", "nk"). A few of them have their first and last names, separated by coma (e.g. firstname.lastname). These are the ones, which have to be deleted from the .csv file.
My goal here is to write a Powershell script, which delete all rows, containing the character "." (dot) and then save the same .csv file by overwritting it.
Since I'm very new to Powershell, I'd highly appreciate a more detailed answer including the potential code. I tried various examples from similar issues, which I found here, but none of them worked and/or I am getting error messages, mostly because my syntax isn't correct.
Additional info. Here is a part of the table.
I tried this code:
Get-Content "D:\file.csv" | Where-Object {$_ -notmatch '\.'} | Set-Content "D:\File.csv"-Force -NoTypeInformation
As Mathias says, it is helpful to see what you have tried so we can help you come to a working result. It is easy to give you something like this:
$csv = Import-Csv -Path C:\Temp\temp.csv -Delimiter ";"
$newCSV = #()
foreach($row in $csv){
if(!$row.username -or $row.username -notlike "*.*"){
$newCSV += $row
}
}
$newCSV | Export-Csv -Path C:\Temp\temp.csv -Delimiter ";" -NoTypeInformation
The above code eliminates rows that have a dot on the username field. It leaves rows with an empty username intact with the 'if(!$row.username' part. But I have no idea whether this is helpful since there is no example CSV file, also there is no way to know what you have tried so far ;)
Note that I always prefer using ";" as delimiter, because opening the file in Excel will already be correctly seperated. If the current file uses ',' as a delimiter, you will need to change that when importing the CSV.
You were very close! For this you don't need a loop, you just need to do it using the correct cmdlets:
(Import-Csv -Path 'D:\file.csv' -Delimiter ';') |
Where-Object { $_.Initials -notmatch '\.' } |
Export-Csv -Path 'D:\file.csv' -Delimiter ';' -Force -NoTypeInformation
Get-Content simply reads a text file and returns the lines as string array, whereas Import-Csv parses the structure and creates objects with properties from the header line.
The brackets around the Import-Csv are needed to ensure the importing/parsing of the file is completely done before piping the results through. Without that, the resulting file may become completely empty because you cannot read and overwrite the same file at the same time.

PowerShell: update O365 AD bulk attributes through csv file

We are trying to bulk update our Azure Active Directory. We have a excel csv list of UserPrincipalNames that we will update the Title, Department, and Office attributes
# Get List of Clinical CMs
$PATH = "C:\Users\cs\Documents\IT Stuff\Project\Azure AD Update\AD-Update-ClinicalCMs-Test.csv"
$CMs = Import-csv $PATH
# Pass CMs into Function
ForEach ($UPN in $CMs) {
# Do AD Update Task Here
Set-Msoluser -UserPrincipalName $UPN -Title "Case Manager" -Department "Clinical" -Office "Virtual"
}
The CSV:
User.1#domain.com
User.2#domain.com
User.3#domain.com
The Set-MsolUser command will work on its own, but it is not working as intended in this For loop. Any help or insight is greatly appreciated
As Jim Xu commented, here my comment as answer.
The input file you show us is not a CSV file, instead, it is a list of UPN values all on a separate line.
To read these values as string array, the easiest thing to is to use Get-Content:
$PATH = "C:\Users\cs\Documents\IT Stuff\Project\Azure AD Update\AD-Update-ClinicalCMs-Test.csv"
$CMs = Get-Content -Path $PATH
Of course, although massive overkill, it can be done using the Import-Csv cmdlet:
$CMs = (Import-Csv -Path $PATH -Header upn).upn

Find and replace a specific string within a specific file type located in wildcard path

Problem:
Update a specific string within numerous configuration files that are found within the subfolders of a partial path using PowerShell.
Expanded Details:
I have multiple configuration files that need a specific string to be updated; however, I do not know the name of these files and must begin my search from a partial path. I must scan each file for the specific string. Then I must replace the old string with the new string, but I must make sure it saves the file with its original name and in the same location it was found. I must also be able to display the results of the script (number of files affected and their names/path). Lastly, this must all be done in PowerShell.
So far I have come up with the following on my own:
$old = "string1"
$new = "string2"
$configs = Get-ChildItem -Path C:\*\foldername\*.config -Recurse
$configs | %{(Get-Content $_) -Replace $old, $new | Set-Content $_FullName
When I run this, something seems to happen.
If the files are open, they will tell me that they were modified by another program.
However, nothing seems to have changed.
I have attempted various modifications of the below code as well. To my dismay, it only seems to be opening and saving each file rather than actually making the change I want to happen.
$configFiles = GCI -Path C:\*\Somefolder\*.config -Recurse
foreach ($config in $configFiles) {
(GC $config.PSPath) | ForEach-Object {
$_ -Replace "oldString", "newString"
} | Set-Content $config.PSPath)
}
To further exasperate the issue, all of my attempts to perform a simple search against the specified string seems to be posing me issues as well.
Discussing with several others, and based on what have learned via SO... the following code SHOULD return results:
GCI -Path C:\*\Somefolder\*.config -Recurse |
Select-String -Pattern "string" |
Select Name
However, nothing seems to happen. I do not know if I am missing something or if the code itself is wrong...
Some questions I have researched and tried that are similar can be found at the below links:
UPDATE:
It is possible that I am being thwarted by special characters such as
+ and /. For example, my string might be: "s+r/ng"
I have applied the escape character that PowerShell says to use, but it seems this is not helping either.
Replacing a text at specified line number of a file using powershell
Find and replacing strings in multiple files
PowerShell Script to Find and Replace for all Files with a Specific Extension
Powershell to replace text in multiple files stored in many folders
I will continue my research and continue making modifications. I'll be sure to notate anything that get's me to my goal or even a step closer. Thank you all in advance.

Powershell how to take string from file and put into a variable

I am writing a Powershell script to "build" Windows 7 PCs: adding users, printers, applications, changing settings, et al. I am adding some printer drivers using PNPUtil, but the problem is I won't know what "Published name" the drivers will be given.
If I put the output from the PNPUtil command into a .txt file, is there a way for me to then take the __.inf Published name and put it into a variable so that I can then use that name to add the printer using $printerclass.CreateInstance()?
You don't have to use a file if PNPUtil only outputs the name your interested in. That is, you can assign its output to a variable like so:
$result = pnputil.exe
BTW if you want to use a file, to read content from a file you use Get-Content:
pnputil.exe > result.txt
$result = Get-Content result.txt
$line = $result | Foreach {if ($_ -match 'assigned an (\w+\.inf)') {$matches[1]}}
Okay - I found my own solution: Once the .inf file is added, all the driver names in that .inf are stored in the Microsoft update files. I just need to know the specific name of the driver I need from each .inf file in order to add the Printers.
However, I'd still love to know how to get a string from a line from a file using Powershell.

Resources