I am new to Powershell, I basically use it to manipulate multiple xml files for my work assignments. I work with proprietary xml-based files, meaning they have their own extensions but are basically xml. I would be grateful if you could help me figure out how you can change the value of the 'xml:lang' attribute in the below example with a ps1 script. Let's assume I have multiple files with the *.flprj extension and they all share below contents:
<?xml version="1.0" encoding="utf-8"?>
<CatapultProject Version="1" xml:lang="en-gb" />
What I would like to achieve is change the value of the xml:lang attribute recursively in all subfolders containing *.flprj files from 'en-gb' to let's say 'nl-nl'. I figured out how to achieve this by replacing strings, but I'd rather have replace value as the source languages might differ. I would much appreciate your suggestions.
Here's an example to get you going. Might have to adjust it depending on your xml (multiple lang-values, locations ++).
Find all flprj-files
Read them and convert to XMLDocument
If CatapultProject.lang equals 'en-gb', modify and save
Ex:
Get-ChildItem -Path "C:\Folder\" -Filter "*.flprj" -Recurse | Foreach-Object {
$xml = [xml](Get-Content $_.FullName)
if($xml.CatapultProject.lang -eq 'en-gb') {
$xml.CatapultProject.lang = "nl-nl"
$xml.Save($_.FullName)
}
}
Related
I get a CSV every week that our finance team puts in a shared drive. I have a script for that CSV that I run once I get it.
The first command of the script is of course Import-Csv.
The problem is, the finance team insists on naming the file differently each time plus they don't always put it in the same location within the drive.
As a result, I have to first hunt for the file, put it into the directory that the script points to and then rename the file.
I've tried talking to the team about putting it in the same location and making sure the filename is the same but they only follow the instructions for a couple of weeks before just doing whatever.
Ideally, I'd like for it so that when I run the script, there would be a popup that would ask me to pick a CSV (Similar to how it looks when you do "Save As" on an Office Document).
Anyway for this to be done within PowerShell?
You can access .Net classes and interface with the forms library to instantiate and take input from the standard FileOpen dialog. Something like below:
Using Namespace System.Windows.Forms
$FileBrowser = [OpenFileDialog]::new()
$FileBrowser.InitialDirectory = 'c:\temp'
$FileBrowser.Filter = 'Comma Separated Values (*.csv) | *.csv'
[Void]$FileBrowser.ShowDialog()
$CsvFile = $FileBrowser.FileName
Then use $CsvFile int he Import-Csv command.
You can change the .InitialDirectory property to make navigating a little more convenient.
Use the .Filter property to limit the file open display to CSV files, to make things that much more convenient.
Also, use the [Void] class to prevent the status return (usually 'OK' or 'Cancel') from echoing to the screen.
Note: A simple Google search will turn up many examples. I refined some of the work from here. That will also document some of the other properties if you want to explore etc.
If you are willing to settle for a selection box that doesn't look as nice as the Save As dialog, you can use Out-Gridview. Something along these lines might help.
$filenames =
#(Get-ChildItem -Path C:\temp -Recurse -Filter *.csv |
Sort-Object LastWriteTime -Descending |
Out-GridView -Title 'Choose a file' -PassThru)
$csvfile = $filenames[0].FullName
Import-Csv $csvfile | More
The -Path specifies a directory that contains all the locations where your csv file might be delivered. The sort is just to put the recently written files at the top of the grid. This supposedly makes selection easier. The #() wrapper merely makes sure the result stored in $filenames is an array.
You would do something else with the results of Import-Csv.
Steven's response certainly satisfies your original question, but an alternative would be to let PowerShell do the work. If you know the drive, and you know the name of the file this week, you can pass the name to your script and let it search the drive filtering on the specific csv file you need. Make it recursive, and open the only file that matches. Sorry, didn't have time yesterday to include code. Here's a function that returns the full file path when provided with a top level search path and a filename with possible wildcards.
function gfp { $result=gci $args[0] -recurse -include $args[1]; return ($result.DirectoryName + "\" + $result.Name) }
Example: gfp "d:\rootfolder" "thisweeksfilename.csv"
so I have updated my PowerShell (downloaded new one from MS Store) to version 7.2.4.0.
And the I wantto convert Markdown file to HTML, so I would import the 2 modules based on this description (https://social.technet.microsoft.com/wiki/contents/articles/30591.convert-markdown-to-html-using-powershell.aspx), so I would do something like:
Import-Module C:\Users\user\Documents\WindowsPowerShell\Modules\powershellMarkdown.dll
Import-Module C:\Users\mitus\Documents\WindowsPowerShell\Modules\MarkdownSharp.dll
Now I want to:
$md = ConvertFrom-Markdown C:\test\test.md
It results with:
ConvertFrom-Markdown: The given key 'test.md' was not present in the dictionary.
So I try the following:
$md = ConvertFrom-Markdown -Path .\test.md
And the POwerShell now says:
ConvertFrom-Markdown: A parameter cannot be found that matches parameter name 'Path'.
Either way, Its not working. Why PowerShell does not know parameter -Path? How do I make conversion from Markup to HTML working? Why is this shit not working at all even if imported those two .dll files? What am I doing wrong?
Thank you for your help!
The ConvertFrom-* commands generally don't support file input directly (with exceptions). When there is no related Import-* command, you have to use Get-Content to first read the file and then pass it to the ConvertFrom-* command like so:
$md = Get-Content C:\test\test.md -Raw | ConvertFrom-Markdown
Make sure to use the -Raw parameter so the ConvertFrom-Markdown command receives a single input string instead of an array of lines, which could be parsed incorrectly.
If you want to inspect the content of the .md file first, you may store it in a separate variable:
$text = Get-Content C:\test\test.md -Raw
# Now you may optionally log the content of the .md file
Write-Host $text
$md = $text | ConvertFrom-Markdown
I have a special need and I feel stuck on that..
Some user will put some file in a directory with several different name, and I need to rename them regarding a special pattern for those files to be consume by another app.
Example:
In directory -> Target
file1-dd-mm-yyyy -> file1
file2 -> thisfile2
flie45224 -> file123
So as you can see this can be some variables, dates, ID etc..
The target will always be the same but the file in source can be different because of date for example.
So first I had 2 files, so I write the script in plain text "If test-path blabla do this else go out" but it seems that now I will have 37 different files with all differents name. So I thought about using an excel sheet(CSV?), but I can't really find any help on this need.
The goal is to have a sheet as "pattern" like if you found file like in 'A1' then rename as in 'A2' etc...
Do you guys have an idea ?
Thanks by advance :)
I understand you need a csv with the following columns:
A1: regex/pattern to match
A2: transform rule which should be
dynamic
The trick is to use scriptblock if you want to use variables like the file name.
Basically, your csv will be:
A1;A2
"^file1-\d\d-\d\d-\d\d\d\d$";"file1"
"^file2$";"this$($file.Name)"
"^flie*";"file123"
And the code would be:
$myRules = Import-Csv "C:\xxx\test.csv" -Delimiter ";"
$files = gci C:\temp\
foreach ($file in $files) {
foreach ($rule in $myRules) {
if ($file.Name -match $rule.A1) {
Write-host "$($file.Name) is matching pattern ""$($rule.A1)"". Applying rename to ""$($rule.A2)"""
$NewScriptBlock = [scriptblock]::Create("rename-item -Path $($file.FullName) -NewName ""$($rule.A2)""")
$NewScriptBlock.Invoke()
}
}
}
Which gives before:
file1-01-02-0344
file2
flie45224
Output during the execution:
file1-01-02-0344 is matching pattern "^file1-\d\d-\d\d-\d\d\d\d$". Applying rename to "file1"
file2 is matching pattern "^file2$". Applying rename to "this$($file.Name)"
flie45224 is matching pattern "^flie*". Applying rename to "file123"
And after:
file1
thisfile2
file123
Explanations
The first foreach is parsing the files. Then for each of those files, we are checking if one of the rule is matching thanks to the -match $rule.A1.
With the first example, you can use regexp (\d to match digits). For the other cases, I kept it simple as you didn't clarify the rules but this will be your homework :)
Then in the transform rules, you can use the filename variable as shown in the second transform rule: this$($file.Name)
NB: it could be a good idea to add a flag to leave the loop for the current file once it has been renamed to avoid unecessary check and to display a message if the file hasn't match any pattern.
Seems a bit odd, I suspect you should be able to use a regex pattern instead but can't say without seeing the csv contents. Otherwise try this. Assumes the CSV has a column headers called First and Second, First matches the basename (no extension) of the file and Second contains the basename you want it changed to.
$csv = Import-Csv -Path c:\filenames.csv
Get-ChildItem -Path c:\temp | %{if($csv.First -contains $_.BaseName){Rename-Item -Path $_.FullName -NewName "$($csv.Second[$csv.First.IndexOf($_.BaseName)]+$_.Extension)"}}
Let's say I have 10 PDF files in a folder named c:\Temp
1440_021662_54268396_1.pdf
1440_028116_19126420_1.pdf
1440_028116_19676803_1.pdf
1440_028116_19697944_1.pdf
1440_028116_19948492_1.pdf
1440_028116_19977334_1.pdf
1440_028116_20500866_1.pdf
1440_028116_20562027_1.pdf
1440_028116_20566871_1.pdf
1440_028116_20573350_1.pdf
In my search, I know I am looking for a file that will match a specific number, for example 19676803 (I'm getting the number to search for from a SQL Query I'm running in my script)
I know how to find that specific file, but what I need to be able to do is move all the files after the searched file has been found to another pre-defined folder. So using the 10 PDFs above as the example files, I need to move all the files "after" the file named 1440_028116_19676803_1.pdf to another folder. I know how to move files using PowerShell, just do not know how to do it after/from a specific file name. Hope that makes sense.
$batchNumCompleted = 'c:\Temp\'
$lastLoanPrinted = $nameQuery.LoanNumber
$fileIndex = Get-ChildItem -path $batchNumCompleted | where {$_.name -match $lastLoanPrinted}
Can anyone provide suggestions/help on accomplishing my goal? I'm not able to provide all code written so far as it contains confidential information. Thank you.
Use the .Where() extension method in SkipUntil mode:
$allFiles = Get-ChildItem -path $batchNumCompleted
$filesToMove = $allFiles.Where({$_.Name -like '*19676803_1.pdf'}, 'SkipUntil') |Select -Skip 1
Remove the Select -Skip 1 command if you want to move the file with 19676803 in the name as well
I have a large amount of shows that I'm looking to organize based off of part of their filename. I would also like to have folders created for those shows if that's at all possible.
I have some basic understanding of Powershell, and don't really know how or where to start it. If you could also include and comments in anything you have/do create so that I can get a better understanding of what you did I would appreciate it.
How about something like this?
Try it Online
'show-episode1.mp4', 'show-episode2.mp4' | % { if ($_ -match '^([a-zA-Z0-9]+)-([a-zA-Z0-9]+).mp4') { "{0}\{1}.mp4" -f $Matches[1], $Matches[2] } }
You can get your list of filenames with something like:
Get-ChildItem -Name
In the directory containing your shows.