I'm looking for a way to find a string in a file, then extract everything between that string and a separator. I can get the LINE out of the file, but I'm a little stumped on how to get the string I want out of it.
Line out of the file is this:
<add key="ConnectionString" value="Data Source=xxxxxx;Initial Catalog=Database;Integrated Security=True" />
I can find the "Data Source=", but I want to return "xxxxxx". Here's what I'm using:
((Get-Process -Name "db-engine").Path ) + '.Config' | Select-String -Path {$_} - Pattern 'Data Source='
This gives me the whole "add key" line from above.
Thanks in advance for any help!
EDIT: Thanks for the responses. After doing some more digging it looks like the "more proper" way is actually to use [xml] to pull the contents of the file into an xml object. Then I can grab values and use split to break the data up into the chunks I need. Final code will look something like this:
$configpath = ((Get-Process -Name "db-engine").Path)
$linepath = $configpath + ".Config"
[xml]$xmlfile = get-content $linepath
$xmlvalue = ($xmlfile.configuration.appSettings.add | where {$_.key -eq "ConnectionString"}).value
$server = $xmlvalue.split(";=")[3]
$db = $xmlvalue.split(";=")[5]
Still working out the kinks, but this seems to get me on the right path. Split breaks the input into an array, so the [x] lets me call a particular element. With multiple delimiters in the split, it breaks the input at any of them. Maybe this can help someone else in the future.
Thanks for all the help, guys!
Personally, I'd just get the value of the attribute -- possibly with an XQuery with Select-Xml -- then split it on semicolons, and then use ConvertFrom-StringData:
$xml = [xml]'<add key="ConnectionString" value="Data Source=xxxxxx;Initial Catalog=Database;Integrated Security=True" />';
$ConnectionString = ($xml | Select-Xml -XPath '/add[#key="ConnectionString"]/#value').Node.'#text'
$DataSource = ($ConnectionString.Split(';') | ConvertFrom-StringData).'Data Source'
I agree with #Bacon Bits - if you have an xml file, then you might want to consider treating it as such. If you want to use Select-String, you can use the MatchInfo's captured groups. Example using a named group:
gc .\dataSource.txt | Select-String -Pattern 'Data Source=(?<ds>.+?);' | % { $_.Matches[0].Groups['ds'].Value }
Related
In input :
i have an \users\myself\desktop\test\file.xslx containing multiples column like this :
ColumnA ColumnB ... ColumnQ (for a total of 17 columns)
each column have some data.
In output :
I would like to have a cmd like this :
New-ADUser -Name $(columnAdata) -GivenName "$(columnBdata)" -Surname "$(columnCdata)" -DisplayName "$(columnDdata)" -SamAccountName "$(columnEdata)" ... etc until -blabla "$(ColumnQdata)"
Is that possible to store de columndata in variables to insert them in a command ?
Thanks a lot.
I would suggest to first change the column headers to be the same as the parameters you intend to use with the New-ADUser cmdlet.
Having matching headers would help greatly in not making mistakes.
Next, save your Excel file as CSV, let's say a file called NewUsers.csv
The code then can be quite simple and easy to maintain:
# import the CSV file using the same separator character as Excel uses on your system
Import-Csv -Path 'X:\NewUsers.csv' -UseCulture | ForEach-Object {
# use Splatting: create a Hashtable with all properties needed taken from the CSV
# see: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_splatting
$userProperties = #{
Name = $_.Name # as opposed to $_.columnAdata
GivenName = $_.GivenName # as opposed to $_.columnBdata
Surname = $_.Surname
DisplayName = $_.DisplayName
SamAccountName = $_.SamAccountName
# etcetera
}
New-ADUser #userProperties
}
I need help comparing two Excel files in Powershell.
I have an Excel-file which contains 6 000 rows and 4-5 columns with headers:
"Number" "Name" "Mobile data".
Let's call it: $Services
Now, I want to compare that file with other Excel-files. For example:
one file containing 50 rows with header columns: "Number", "Name", etc.
Let's call it $Department
The important thing is that in $Services, it contains more important columns like "Mobile data",
so my mission is to compare column: "Number" from $Services with column "Number" from each other Excel file.
Then if they match, write "the whole row" from $Services
I'm not that familiar with Excel, so I thought, this should be possible to do in Powershell.
I'm novice in Powershell, so I only know basic stuff. I'm not that familiar with pscustomobject and param.
Anyway, what I tried to do was to first declare them in variables with ImportExcel:
$Services = Import-Excel -Path 'C:\Users\*.xlsx'
$Department = Import-Excel -Path 'C:\Users\*.xlsx'
Then I made a foreach statement:
foreach ($Service in $Services) {
if (($Service).Number -like ($Department).Number)
{Write-Output "$Service"}
}
The problem with this is that it is collecting all empty columns from ($Services).Number and writing the output of each row in $Services.
I tried to add a nullorEmpty to $Department, if the .Number is empty, but it didn't make any difference. I also tried to add that if the row is empty in .Number, add "1234", but still it collects all .Number that is empty in $Services.
I also tried to do a: $Services | ForEach-Object -Process {if (($_).Number -match ($Department).Number)
{Write-Output $_}} But it didn't match any. When I tried -notmatch it took all.
I don't know but it seems that I have to convert the files to objects, like the columns to object so each string becomes an object. But right now my head is just spinning and I need some hints on where I can start with this.
I would recommend downloading the Module ImportExcel from the PSGallery.
Import-Excel can easily import your Excel sheet(s) to rows of objects, especially if your sheets are 'clean', i.e., only contain (optional) headers and data rows.
Simply import the cells to PowerShell objects and use Compare-Object to discover differences.
EDIT (after reading the additional questions by poster in the comments):
To compare using specific properties you'll need to add these to the Compare-Object parameters.
Using a trivial "PSCustomObject" to create a simple set of objects to show this idea it might look like this:
$l = 1..4 | ForEach-Object { [pscustomobject]#{a=$_;b=$_+1} }
$r = 1,2,4,5 | ForEach-Object { [pscustomobject]#{a=$_;b=$_+1} }
compare-object $l $r -Property B
B SideIndicator
- -------------
6 =>
4 <=
You may also compare multiple properties this way:
compare-object $l $r -Property A,B
A B SideIndicator
- - -------------
5 6 =>
3 4 <=
FYI: I find myself typing "Get-Command -Syntax SomeCommand" so often every day that I just made a function "Get-Syntax" (which also expands aliases) and then aliased this to simply "syn".
90% of the time once you understand the structure of PowerShell cmdlets (at least well-written ones) there is no need to even look at the full help -- the "syntax" blocks are sufficient.
Until then, type HELP (Get-Help) a lot -- 100+ times per day. :)
So the solution for my whole problem was to add -PassThru.
Because my mission was to compare the numbers of the two Excel-files, select the numbers that equals and then take all the properties from one file. So my script became like this:
$Compare = Compare-Object $Services $Department -Property Numbers -IncludeEqual -ExcludeDifferent -PassThru
$Compare | Export-Excel -Path 'C:\Users\*
But I wonder, -PassThru sends all the objects from ReferenceObject, how can I send all the objects from DifferenceObject?
I am using powershell script to check and see which java file in the directory is the main java file by checking the contents of each file and seeing if it contains "main". The problem is, when the script hits the Contains() method line, it returns false, even when I can see, when debugging, that the contents of my $javafilecontents clearly has the phrase "main".
What am I missing, or how to I get Contains to return true for this case?
Just to test thoroughly, I also tried other small key words that should be in the file such as $javafilecontents.Contains("import"), which is literally the very first word in the file, but it still returns false
I found a similar situation here:
Why is string.contains() returning false?
I assume this might have something to do with the string being too long. In their advice, they say to add the # symbol in front of the long string. I haven't tested this yet because I'm not sure how I would do that since my long string is a variable set from Get-Content
Code:
foreach ($userjavafile in $userjavafiles)
{
$javafilepath = "" + $destination + "\" + $userjavafile
$javafilecontents = Get-Content -Path $javafilepath
$mybool = ($javafilecontents.Contains("main"))
if ($mybool)
{
$mainjavafile = $userjavafile
}
$spaceseparatedjavafiles += "" + $userjavafile + " "
}
$destination is the path to the files that I created earlier in the code.
$javafilecontents has the contents of a .java file which includes the line "public static void main(String[] args){"
These two are tested to be correct since I can see in the debugger that the contents are correctly placed into the variable.
$mybool stays false, but the way I thought I understand Contains() it should be true
Get-Content -Path $javafilepath produces an array of strings where each line of the file is an array element. You will have issues using string method Contains() as PowerShell is actually using the Contains() method of the Array class. For the Array class Contains() method to return true, you would have to match an entire line.
Get-Content -Path $javafilepath -Raw will produce better results because it produces a single string. Then the string class Contains() method will allow for a substring match. Keep in mind the string class method is case-sensitive.
$javafilecontents = Get-Content -Path $javafilepath -Raw
$mybool = $javafilecontents.Contains("main")
The other option is to continue without the -Raw switch and loop through each line. Then you can use the string class Contains() against each line, which will be a single string. The where() method can filter an array based on a condition. Then you can cast the success of that filter to [bool]. A match will yield True and no match will yield False.
$javafilecontents = Get-Content -Path $javafilepath
$mybool = [bool]($javafilecontents.where{$_.Contains("main")})
The same approach as the above method can be used with the case-insensitive -match operator, which produces a cleaner solution. -cmatch is the case-sensitive version.
$javafilecontents = Get-Content -Path $javafilepath
$mybool = [bool]($javafilecontents -match 'main')
I have a version number in a SQL file that I need to replace via PowerShell. In the file it looks like so:
values
('Current', '15.7.1.0'),
('New', '15.7.22.0')
I need to replace it with whatever the current version is, but due to the fact that I don't know what the current version number is going to be, nor do I know its exact length (digit count can change as leading zeroes are not used), I need to use wildcards, and what I'm trying isn't working.
Currently this is what I'm trying:
$content = Get-Content C:\Users\user\Desktop\sql\file.sql
$newContent = $content -replace "'Current', '*'", "'Current', '$newVersion'"
And it actually finds it and replaces it, but not all of it for some reason. After running that, what I get is:
values
('Current', '16.6.21.0'15.7.1.0'),
('New', '15.7.22.0')
So it definitely finds the correct place in the file and does a replace on some of it, but doesn't actually replace the version number. Can anyone tell me what I'm doing wrong?
As #PetSorAl said, -replace uses wildcards not regular expressions.
Below is an example based on yours.
Example
$content = #"
values
('Current', '15.7.1.0'),
('New', '15.7.22.0')
"#
$newVersion = '16.6.21.0'
$newContent = $content -replace "'Current', '\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}'", "'Current', '$newVersion'"
Write-host $newContent
Results
values
('Current', '16.6.21.0'),
('New', '15.7.22.0')
I've found myriad methods to retrieve data FROM a string with substrings, but what I want to do is create a new string that contains substrings. The reason for this is that I want to pass that string to a CSV through the Export-CSV cmdlet. This is in a PowerShell Forms created app.
So the plan would be to
1). Read the contents of each text box:
(e.g. $endusername.text $endusernumber.text $locationname.text)
2). Store those into a new string with substrings
($formoutput.endusername $formoutput.endusernumber $formoutput.locationname)
3). Output the string to a .CSV
Export-CSV -InputObject $formoutput "c:\output\formoutput.csv"
Basically, if I take any existing cmdlet (say, Get-Mailbox), store its output as a string, and then pass that string through the Export-CSV in the way explained above, it performs exactly the way I like - creating a .CSV with each of the substrings as a column, and the contents of that substring in the appropriately headed column. I just want to be able to do that with a string containing substrings that I define.
I think you are confusing nomenclature a little bit. It sounds like what you want is a custom object not a string. Here is some pseudo-code to get you going in the right direction:
$formOutput = New-Object PsCustomObject -Property #{'EndUserName' = $endUserName.Text;
'EndUserNumber' = $endUserNumber.Text;
'LocationName' = $locatioName.Text}
$formOutput | Export-CSV .\FileName.csv -NoTypeHeader