in my script I want to read a csv-file into an array and split the text in the first column.
The file consists a table with 2 columns. In the first column there are the personal names with the short Usernames in brackets. In the second column are the position of the user.
User:
Hoch,Susane (HOCH05)
Albrecht, Melanie (ALBRE05)
Department:
Managment
Salesoffice
I read the first column in an array and want to split every char after the first "(". So the I have got "Hoch, Susanne" instead of "Hoch, Susane (HOCH05)".
I get the following error message:
[Selected.System.Management.Automation.PSCustomObject] contains no method with the name "Split".
The type of the variable "$value is:
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True False PSCustomObject System.Object
I canĀ“t find my misstake.
Here is my code:
$Arrayusername_ads_unique = New-Object System.Collections.ArrayList
$AD_User = New-Object System.Collections.ArrayList
$AD_User_table = New-Object System.Collections.ArrayList
$username_AD = New-Object System.Collections.ArrayList
$Arrayusername_ads_unique = Get-Content -Path "C:\temp\Userabgleich\Liste-original\User_ADS-utf8.csv"
$Arrayusername_ads_unique | Out-File C:\temp\Userabgleich\output-temp\User_ADS-utf8.csv -Append -Encoding utf8
$AD_User = Import-CSV 'C:\temp\Userabgleich\output-temp\User_ADS-utf8.csv' -Delimiter ";" | sort User
$AD_User_table = $AD_User | Select-Object User
foreach ($value in $AD_User_table)
{
$value.GetType()
$username_AD = $value.Split("(")
}
You can modify your foreach loop to achieve the results:
foreach ($value in $AD_User_table)
{
($value.user -split "\(")[0]
}
I am splitting on the .user property of $value to retrieve the value you are after in string format. By default, $value is going to be a [PSCustomObject] with a property called User. I am retrieving index 0 ([0]) because your -split match will consume a line of output whether or not you choose to keep the output.
If you are only looping to retrieve this particular result, you can accomplish this without a loop using regex substitution and named captures:
$ad_user_table.user -replace "(?<Name>.*?)\(.*",'${Name}'
since you did not provide a proper CSV to test against, i made a guess at what it would look like. [grin]
what it does ...
uses the .Split() method to split on the (
takes the 1st result of that split
trims away any leading/trailing whitespace
sends the result to the $Names collection
displays the content of that collection
here's the code ...
# fake reading in a CSV file
# in real life, use Import-CSV
$InStuff = #'
User,Department
"Hoch,Susane (HOCH05)","Managment"
"Albrecht, Melanie (ALBRE05)","Salesoffice"
'# | ConvertFrom-Csv
$Names = foreach ($IS_Item in $InStuff)
{
$IS_Item.User.Split('(')[0].Trim()
}
$Names
output ...
Hoch,Susane
Albrecht, Melanie
Related
In my script, I am building a custom Powershell object which will be sent to Export-Csv. The receiving party has required that I include some blank columns (no data and no header) and I have no idea how to do that.
If the object looks like this:
$obj = [PSCustomObject][ordered]#{
EMPLOYER_EIN = '123456'
ACTION_CODE = 1
LAST_NAME = Smith
FIRST_NAME = John
MIDDLE_INITIAL = $null
EMPLOYEE_SSN = '111-11-1111'
}
How can I have the resulting .csv file's first row look like this:
EMPLOYER_EIN,ACTION_CODE,,LAST_NAME,FIRST_NAME,MIDDLE_INITIAL,,EMPLOYEE_SSN
Put another way, after I run Export-Csv, I want the file to look like this when opened in Excel:
EMPLOYER_EIN
ACTION_CODE
LAST_NAME
FIRST_NAME
MIDDLE_INITIAL
EMPLOYEE_SSN
123456
1
Smith
John
111-11-1111
Note the extra columns between action_code/last_name and middle_initial/employee_ssn. I am using PS 5.1 but could use 7 if necessary.
As a test, I created a CSV test.csv with fields A,B, and C, and put a couple of lines of values:
"A","B","C"
1,2,3
4,5,6
I then executed the sequence of commands
Import-CSV -path Test.csv | Select-Object -Prop A," ",B,C | Export-CSV -Path test2.csv
and looked at the resultant test2.csv, which contained
#TYPE Selected.System.Management.Automation.PSCustomObject
"A"," ","B","C"
"1",,"2","3"
"4",,"5","6"
I believe that this is going to be the closest you'll get without manually processing the CSV as a text file.
This is essentially what Santiago Squarzon was suggesting in the comments.
If you need multiple "blank" columns, each one will have to have a header with a different non-zero number of spaces.
I suggest:
constructing the object with blank dummy properties with a shared name prefix, such as BLANK_, followed by a sequence number (the property names must be unique)
initially piping to ConvertTo-Csv, which allows use of a -replace operation to replace the dummy property names with empty strings in the first output line (the header line).
the result - which already is in CSV format - can then be saved to a CSV file with Set-Content.
$obj = [PSCustomObject] #{
EMPLOYER_EIN = '123456'
ACTION_CODE = 1
BLANK_1 = $null # first dummy property
LAST_NAME = 'Smith'
FIRST_NAME = 'John'
MIDDLE_INITIAL = $null
BLANK_2 = $null # second dummy property
EMPLOYEE_SSN = '111-11-1111'
}
$first = $true
$obj |
ConvertTo-Csv |
ForEach-Object {
if ($first) { # header row: replace dummy property names with empty string
$first = $false
$_ -replace '\bBLANK_\d+'
}
else { # data row: pass through
$_
}
} # pipe to Set-Content as needed.
Output (note the blank column names after ACTION CODE and MIDDLE_INITIAL):
"EMPLOYER_EIN","ACTION_CODE","","LAST_NAME","FIRST_NAME","MIDDLE_INITIAL","","EMPLOYEE_SSN"
"123456","1",,"Smith","John",,,"111-11-1111"
I would like to convert the content of this file (the first two columns, BARCODE and LOCATION) to object, but I am stuck creating the object..
#BARCODE LOCATION LIBRARY STATUS COPY
#------- -------- ------- ------ ----
#L40001L8 slot 41 DRP_TAPE_DRPLTO Full Primary_Global
#L40002L8 slot 96 DRP_TAPE_DRPLTO Full Primary_Global
#L40034L8 IEPort2 DRP_TAPE_DRPLTO Full Primary_Global
$object= #{}
ForEach ($i in $(gc "C:\temp\tapes.txt"))
{
$object.BARCODE= ($i.ToString().Substring(0,8))
$object.LOCATION= ($i.ToString().Substring(0,19))
New-Object psobject -Property $object
}
Can anyone help me?
Use creating object from hashtable:
$ht = #{ 'key1' = 'value1'; key2 = 'value2' }
$ht['key3'] = 'value3'
$o = [PSCustomObject]$ht
When you have fixed-width columns:
$lines = #(#'
#BARCODE LOCATION LIBRARY STATUS COPY
#------- -------- ------- ------ ----
#L40001L8 slot 41 DRP_TAPE_DRPLTO Full Primary_Global
#L40002L8 slot 96 DRP_TAPE_DRPLTO Full Primary_Global
#L40034L8 IEPort2 DRP_TAPE_DRPLTO Full Primary_Global
'# -split "`r`n" )
# $lines = [System.IO.File]::ReadAllLines($filePath)
$lines = $lines | Select-Object -Skip 2
$objects = $lines | % {
return [PSCustomObject]#{
BARCODE = $_.Substring(1,8).Trim()
LOCATION = $_.Substring(13,8).Trim()
}
}
$objects
First of all, The content of the file looks very much like you have started off with an array of objects, then 'stringyfied' that using Format-Table and copy/pasted the output from the console to your textfile.
Why every line is now commented by placing # in front, I don't know. Perhaps you did this to format the question in SO?
What I'm trying to say is that if that is indeed what happened, then it would make much more sense to change the script you used to create the file by removing the Format-Table from the code and saving the object array using a cmdlet like Export-Csv, or by saving as JSON or XML..
Anyway, if you have no choice in the matter and need to convert that file back to objects, you can do this:
# read the file line-by-line and replace the whitespaces with a comma
$data = switch -Regex -File 'D:\Test\File1.txt' {
'^#[^-]' { $_.TrimStart("#") -replace '\s+', ',' }
# if the comment marks '#' are NOT in the file, use this instead
# '^[^-]' { $_ -replace '\s+', ',' }
}
$result = $data | ConvertFrom-Csv | Select-Object BARCODE, LOCATION
# output on screen
$result
# output to CSV file
$result | Export-Csv -Path 'D:\Test\Output1.csv' -UseCulture -NoTypeInformation
Output on screen will look like
BARCODE LOCATION
------- --------
L40001L8 slot
L40002L8 slot
L40034L8 IEPort2
You can simply double-click the Output1.csv file to open in Excel
$dat = query user /server:$SERVER
this query gives below data
USERNAME SESSIONNAME ID STATE IDLE TIME LOGON TIME
>vm82958 console 1 Active 1:28 2/9/2018 9:18 AM
adminhmc 2 Disc 1:28 2/13/2018 10:25 AM
nn82543 3 Disc 2:50 2/13/2018 3:07 PM
I would like to get each independent user details like STATE, USERNAME, ID details. I tried below code but it is not giving any data
foreach($proc in $dat) {
$proc.STATE # This is not working this command not giving any data.
$proc.ID # This is not working this command not giving any data.
}
Please help me on this.
The result of $dat.GetType() is:
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True String System.Object
This is very similar to this StackOverflow post, but you have blank fields in your data.
One solution is to deal with this first. Example below but this may break given data that is very different to your example. For a more robust and complete solution see Matt's comment
# replace 20 spaces or more with TWO commas, because it signifies a missing field
$dat2 = $dat.Trim() -replace '\s{20,}', ',,'
# replace 2 spaces or more with a single comma
$datTable = $dat2.Trim() -replace '\s{2,}', ',,'
foreach($proc in $datTable) {
$proc.STATE
$proc.ID
}
Another option is to use fixed Columns with string.Insert , like this:
$content = quser /server:$SERVER
$columns = 14,42,46,54,65 | Sort -Descending
$Delimiter = ','
$dat = $content | % {
$line = $_
$columns | % {
$line = $line.Insert($_, $Delimiter)
}
$line -replace '\s'
} |
ConvertFrom-Csv -Delimiter $Delimiter
And Then:
foreach($proc in $dat) {
$proc.STATE
$proc.ID # Will show the relevant Data
}
I'm new to Powershell, and this behavior has me stumped.
Let's say I've created a hashtable with the following information:
$hash= #{
California = 'Sacramento';
Washington = 'Olympia';
Oregon = 'Salem';
Alaska = 'Juneau';
}
$hash
Now I want to search for a particular key using a where -match statement:
$result = #($layers.getEnumerator() | ? {$_.key -match 'Cal'})
$result
This returns with California for the key and Sacramento for the value. All is working as it should.
Now, instead of typing in the keys and values for the table, let's say I want to import them into the hashtable using a CSV file:
California,Sacramento
Washington,Olympia
Oregon,Salem
Alaska,Junea
Here is my code I created to import the CSV, which works fine:
Import-Csv C:\Users\hgordon\Desktop\Maps_to_search\junk.csv -Header "Key","Value"
$layers = #{}
Foreach ($key in $keys) {
$layers[$key] = $csv
}
My problem is that when I run my search statement above, it returns nothing.
Why won't the search work on the imported hashtable? Is there something that I need to modify with the import statement? What is the difference between an imported hashtable vs one that is manually typed?
Thanks!
Edit: I've edited my import script as follows to make it clearer:
$t = Import-Csv -Path C:\Users\hgordon\Desktop\Maps_to_search\junk.csv -Header "Key","Value"
$HashTable = #{}
foreach($r in $t)
{
#Write-Host $r.column1 $r.column2
$HashTable[$r.Key] = $r.Value
}
$HashTable
I still get no result from my search script, though.
To get the job done, you need just to get the output of Import-Csv and use it this way:
$layers = Import-Csv "C:\yourfile.csv" -Header "Key","Value"
$result = #($layers | ? {$_.Key -match 'Cal'})
$result
You don't need to create a HashTable.
So in your current iteration, Import-Csv is importing the CSV as a PSCustomObject type. Your Key/Value headers are being used to create a property on the object with an array under each of the CSV, so you end up with:
key value
--- -----
California Sacramento
Washington Olympia
Oregon Salem
Alaska Junea
TypeName: System.Management.Automation.PSCustomObject
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
key NoteProperty string key=California
value NoteProperty string value=Sacramento
To build your own hashtable from this information, you'd need to merge it yourself:
$HT = #{}
$Csv = Import-Csv ... -Header 'Keys','Values'
For ($i = 0; $i -lt $Csv.Keys.Count; $i++)
{
## Here we're using array-accessors
$HT[$Csv.Keys[$i]] = $Csv.Values[$i]
#alternative
#$HT.($Csv.Keys[$i]) = $Csv.Values[$i]
}
I have a csv with a list of usernames
I want to import just one cell of the csv file e.g. A2
Is it possible to be that specific? I have tried googling for this but don't see an exact solution. Tried powershell help also.
Can this be done ?
Thanks
Confuseis
The below example will select and output only 'cell' A2 from test.csv
In this example, the column header for row A is 'username'
$inFile = Import-Csv c:\Temp\test.csv
$targetCell = $inFile.username[1]
Write-Output $targetCell
This snippet is doing the following:
Import the csv file, yielding a PowerShell object.
Select the column you want to work with, the items from that column can be treated as an array. Select the desired item in that column by referring to it's zero based index value.
Output the results.
Import-CSV creates an array of objects from the input file. The column labels in the first row of the CSV become the property names. The other rows are objects in the array. Like any array you can call one element using brackets.
$arrUsers = Import-CSV c:\temp\users.csv
$arrUsers[1]
The second command, above, prints the second object, since counting starts with 0. This object came from the third line of the CSV file, since the first was used as column headers.
If you use Get-Member, it will show you the members (properties and methods) of an object.
$arrUsers | Get-Member
Assuming one of the members is username, combine this with array indexing, you can use:
$arrUsers[1].username
Import-CSV is a very flexible tool. Especially combined with Foreach and Export-CSV. Between Get-Help and Get-Member, you can explore Powershell with ease. Good luck.
When you use Import-Csv you convert the content into a PSCustomObject.
Examples on the following table:
PS> $csv = Import-Csv .\test.csv
PS> $csv
ProcessName Id WS CPU
----------- -- -- ---
sihost 5996 30015488 44.640625
pia_nw 11064 10620928 52.921875
pia_nw 2344 7933952 104.0625
RuntimeBroker 6500 77500416 177.34375
SettingSyncHost 6736 5074944 202.796875
explorer 6600 284934144 272.140625
ipoint 920 3162112 372.78125
rubyw 10648 18026496 389.46875
pia_nw 3108 31330304 1640.5625
OneDrive 10208 33206272 6422.4375
So you will need a NoteProperty name to call a value you're looking for.
PS> $csv.ProcessName[0]
sihost
Another way is to make a header array and use that to slice the data.
If working with a an object:
PS> $header = ($csv | ConvertTo-Csv -NoTypeInfo)[0] -replace '"' -split ",";
>>
PS> $header
ProcessName
Id
WS
CPU
Or if working with the file:
PS> $header = (gc .\test.csv)[0] -replace '"' -split ',';
ProcessName
Id
WS
CPU
Then just use the appropriate index:
PS> $csv[0]."$($header[0])"
sihost
Finally there is the Excel.Application ComObject method on an xlsx file. This will let you select cell's and ranges.
PS> $file = "C:\Some\Path\IMade\Up\test.xlsx"
PS> $objExcel = New-Object -ComObject Excel.Application
PS> $objExcel.Visible = $false
PS> $wb = $objExcel.Workbooks.Open($file)
PS> $ws = $wb.Sheets.Item(1)
PS> $ws.Range("A2").Text
sihost
More info on using the ComObjects can be found here:
Application Object (Excel)