Copy Specific column from csv file to another file with specific word location using powershell - powershell

I am new to PowerShell its just been a day that I started using this, I need some help regarding building script for my model. These are the steps
I am taking file content and I have taken everything in different variable like:
$Test_Name, $Test_number, $Result and consequently imported the data from CSV into these variables:
Import-Csv .\result.csv | ForEach-Object {($Test_Result += $_."Test Result"),($Test_number +=$_."Test Number")
.... and so on
Now I have data into some powershell variables and now I want these variable content to be pasted to another file named output.txt/docs upon several specific parameters like %%PasteHereTestName%%, %%PasteHereTestNumber%%.
Can anyone guide me on this, I have tried several ways but it seems like I need some guidance.
**this will be used by Azure devops pipeline to copy the content from test file to output doc file..

Import the CSV into a variable and you can address each attribute directly, and write individual columns/attributes to a new CSV file by including the select-object statement
$Results = Import-csv c:\temp\testfile.csv
$results | select-object Column1, Column2, Column3 | export-csv C:\temp\proddata.csv

Related

Powershell: how to retrieve powershell commands from a csv and execute one by one, then output the result to the new csv

I have a Commands.csv file like:
| Command |
| -----------------------------------------------|
|(Get-FileHash C:\Users\UserA\Desktop\File1).Hash|
|(Get-FileHash C:\Users\UserA\Desktop\File2).Hash|
|(Get-FileHash C:\Users\UserA\Desktop\File3).Hash|
Header name is "Command"
My idea is to:
Use ForEach ($line in Get-Content C:\Users\UserA\Desktop\Commands.csv ) {echo $line}
Execute $line one by one via powershell.exe, then output a result to a new .csv file - "result.csv"
Can you give me some directions and suggestions to implement this idea? Thanks!
Important:
Only use the technique below with input files you either fully control or implicitly trust to not contain malicious commands.
To execute arbitrary PowerShell statements stored in strings, you can use Invoke-Expression, but note that it should typically be avoided, as there are usually better alternatives - see this answer.
There are advanced techniques that let you analyze the statements before executing them and/or let you use a separate runspace with a restrictive language mode that limits what kinds of statements are allowed to execute, but that is beyond the scope of this answer.
Given that your input file is a .csv file with a Commands column, import it with Import-Csv and access the .Commands property on the resulting objects.
Use Get-Content only if your input file is a plain-text file without a header row, in which case the extension should really be .txt. (If it has a header row but there's only one column, you could get away with Get-Content Commands.csv | Select-Object -Skip 1 | ...). If that is the case, use $_ instead of $_.Commands below.
To also use the CSV format for the output file, all commands must produce objects of the same type or at least with the same set of properties. The sample commands in your question output strings (the value of the .Hash property), which cannot meaningfully be passed to Export-Csv directly, so a [pscustomobject] wrapper with a Result property is used, which will result in a CSV file with a single column named Result.
Import-Csv Commands.csv |
ForEach-Object {
[pscustomobject] #{
# !! SEE CAVEAT AT THE TOP.
Result = Invoke-Expression $_.Commands
}
} |
Export-Csv -NoTypeInformation Results.csv

Powershell script to use input from .txt or .csv to find a value in a 2nd .csv file

Group,
First time posting a question so please be patient. Attempting to use Powershell to read 1 .csv file and search a second .csv for specific data associated with what I passed from the first .csv file. Lets say that both files contain a column with a heading of 'name' with a list of PC names in that column. The 2nd CSV file in addition has a column with a heading named 'id' and that contains a unique ID associated with that PC name (and to complicate matters there may be duplicate PC Names but each one has a unique ID). I want to work through all the PC names in the first .csv file searching for the corresponding unique id of that PC name in the second .csv file and then set that as a variable so that I can perform some action against it. The code below is what I have so far, I'm manually providing the PC name as I cannot seem to get-content of the first file and pass it to the 2nd file. It will get the unique ID but I cannot seem to get it into a variable. Any help would be appreciated.
$computer = "T4763110"
$csv = Import-Csv "C:\Devices.csv"
$csv | Where-Object { $_.name -eq $computer } | % id
1st CSV file
First CSV file
2nd CSV file
Second CSV file
You forgot to specify a >Delimiter< in your import-csv. You can look your delimiter up, when you open your .csv file with the basic Microsoft-Editor.
Mine looks like this:
ID;Name
1;Alienware
2;Lexware
3;One
4;Chip
5;Stack
6;Linux
I made a new table named dev.csv for myself with following content:
When executing this code:
> $csv = Import-Csv "C:/Users/Desktop/dev.csv" -Delimiter ';'
> $csv
Output:
ID Name
-- ----
1 Alienware
2 Lexware
3 One
4 Chip
5 Stack
6 Linux
Then execute this:
> $csv | where-object {$_.name -eq "Alienware"} | % id
1
To save it to a variable, simply put brackets around it.
> $idvar = ($csv | where-object {$_.name -eq "Alienware"} | % id)
> $idvar
1

powershell incremental lines in csv

I have a csv journal file like this:
and I would like to add incremental line numbering like the below:
Can anyone suggest the easiest way to do this through PowerShell so that every time a file is generated it will automatically insert a column called LINENUMBER and insert the incremental numbering?
All help greatly appreciated.
You could do this:
Import-CSV SomeFile.csv | Select *,LINENUMBER | ForEach-Object -Begin { $Line = 1 } {
$_.LineNumber = $Line++
$_
} | Export-CSV SomeFile.csv
Uses Import-CSV to load the CSV file as a PowerShell object and then Select-Object to return all of it's properties and add an additional property named 'LINENUMBER'
Iterates through each line of the CSV, incrementing the new LINENUMBER property with a counter variable named $Line (which is set to 1 before it iterates).
Exports the result as CSV overwriting the original (or you could here redirect it to a new file name).
Note that the new column will be last. You could make it the first column by changing the Select part to Select LINENUMBER,*. If you want it to sit somewhere in the middle that would be slightly more complicated (and likely involve knowing what headers the input file had in advance).

Rename Files with Index(Excel)

Anyone have any ideas on how to rename files by finding an association with an index file?
I have a file/folder structure like the following:
Folder name = "Doe, John EO11-123"
Several files under this folder
The index file(MS Excel) has several columns. It contains the names in 2 columns(First and Last). It also has a column containing the number EO11-123.
What I would like to do is write maybe a script to look at the folder names in a directory, compare/find an associated value in the index file(like that number EO11-123) and then rename all the files under the folder using a 4th column value in the index.
So,
Folder name = "Doe, John EO11-123", index column1 contains same value "EO11-123", use column2 value "111111_000000" and rename all the files under that directory folder to "111111_000000_0", "111111_000000_1", "111111_000000_2" and so on.
This possible with powershell or vbscript?
Ok, I'll answer your questions in your comment first. Importing the data into PowerShell allows you to make an array in powershell that you can match against, or better yet make a HashTable to reference for your renaming purposes. I'll get into that later, but it's way better than trying to have PowerShell talk to Excel and use Excel's search functions because this way it's all in PowerShell and there's no third party application dependencies. As for importing, that script is a function that you can load into your current session, so you run that function and it will automatically take care of the import for you (it opens Excel, then opens the XLS(x) file, saves it as a temp CSV file, closes Excel, imports that CSV file into PowerShell, and then deletes the temp file).
Now, you did not state what your XLS file looks like, so I'm going to assume it's got a header row, and looks something like this:
FirstName | Last Name | Identifier | FileCode
Joe | Shmoe | XA22-573 | JS573
John | Doe | EO11-123 | JD123
If that's not your format, you'll need to either adapt my code, or your file, or both.
So, how do we do this? First, download, save, and if needed unblock the script to Import-XLS. Then we will dot source that file to load the function into the current PowerShell session. Once we have the function we will run it and assign the results to a variable. Then we can make an empty hashtable, and for each record in the imported array create an entry in the hashtable where the 'Identifier' property (in your example above that would be the one that has the value "EO11-123" in it), make that the Key, then make the entire record the value. So, so far we have this:
#Load function into current session
. C:\Path\To\Import-XLS.ps1
$RefArray = Import-XLS C:\Path\To\file.xls
$RefHash = #{}
$RefArray | ForEach( $RefHash.Add($_.Identifier, $_)}
Now you should be able to reference the identifier to access any of the properties for the associated record such as:
PS C:\> $RefHash['EO11-123'].FileCode
JD123
Now, we just need to extract that name from the folder, and rename all the files in it. Pretty straight forward from here.
Get-ChildItem c:\Path\to\Folders -directory | Where{$_.Name -match "(?<= )(\S+)$"}|
ForEach{
$Files = Get-ChildItem $_.FullName
$NewName = $RefHash['$($Matches[1])'].FileCode
For($i = 1;$i -lt $files.count;$i++){
$Files[$i] | Rename-Item -New "$NewName_$i"
}
}
Edit: Ok, let's break down the rename process here. It is a lot of piping here, so I'll try and take it step by step. First off we have Get-ChildItem that gets a list of folders for the path you specify. That part's straight forward enough. Then it pipes to a Where statement, that filters the results checking each one's name to see if it matches the Regular Expression "(?<= )(\S+)$". If you are unfamiliar with how regular expressions work you can see a fairly good breakdown of it at https://regex101.com/r/zW8sW1/1. What that does is matches any folders that have more than one "word" in the name, and captures the last "word". It saves that in the automatic variable $Matches, and since it captured text, that gets assigned to $Matches[1]. Now the code breaks down here because your CSV isn't laid out like I had assumed, and you want the files named differently. We'll have to make some adjustments on the fly.
So, those folder that pass the filter will get piped into a ForEach loop (which I had a typo in previously and had a ( instead of {, that's fixed now). So for each of those folders it starts off by getting a list of files within that folder and assigning them to the variable $Files. It also sets up the $NewName variable, but since you don't have a column in your CSV named 'FileCode' that line won't work for you. It uses the $Matches automatic variable that I mentioned earlier to reference the hashtable that we setup with all of the Identifier codes, and then looks at a property of that specific record to setup the new name to assign to files. Since what you want and what I assumed are different, and your CSV has different properties we'll re-work both the previous Where statement, and this line a little bit. Here's how that bit of the script will now read:
Get-ChildItem c:\Path\to\Folders -directory | Where{$_.Name -match "^(.+?), .*? (\S+)$"}|
ForEach{
$Files = Get-ChildItem $_.FullName
$NewName = $Matches[2] + "_" + $Matches[1]
That now matches the folder name in the Where statement and captures 2 things. The first thing it grabs is everything at the beginning of the name before the comma. Then it skips everything until it gets tho the last piece of text at the end of the name and captures everything after the last space. New breakdown on RegEx101: https://regex101.com/r/zW8sW1/2
So you want the ID_LName, which can be gotten from the folder name, there's really no need to even use your CSV file at this point I don't think. We build the new name of the files based off the automatic $Matches variable using the second capture group and the first capture group and putting an underscore between them. Then we just iterate through the files with a For loop basing it off how many files were found. So we start with the first file in the array $Files (record 0), add that to the $NewName with an underscore, and use that to rename the file.

why export-csv returns a file that contains wierd data?

I have a binary cmdlet Get-CustomPSObject. When I do something like:
Get-CustomPSObject > a.txt
the result is stored as a plain text, meaning that the Get-CustomPSObject is working fine.
However when I try:
Get-CustomPSObject | Export-csv a.csv
The a.csv file becomes:
"Capacity","Count","IsReadOnly","IsFixedSize","SyncRoot","IsSynchronized"
"4","1","False","False","System.Object","False"
none of these fields are in my PSObject. I've no idea what they stands for. Any thoughts?
Export-CSV takes the first object it recieves to create the headers. Most likely, your Get-CustomPSOjbect runs a method/cmdlet/script that returns an object you didn't save. E.g. you use something like
get-childitem
and not
$files = get-childitem
inside your Get-CustomPSObject function.
EDIT
Okay, so you're cmdlet is a binary cmdlet. Important information. I'm no expert in this, so please correct me if I'm wrong.
When you make a binary cmdlet that can output multiple objects, you need to write them one by one. One of the ideas behind PowerShell is the use of a pipeline that can use objects as they come without waiting for the complete array.
Because of your current "design flaw" in your binary cmdlet, Export-CSV tries to export the array(as one item) to a csv-file and not the elements inside.
You now use this:
WriteObject(list/array of objects)
This is bad. It outputs all objects at the same time.
To fix it, run this at the end of your "object-creation-loop":
WriteObject(mycurrentobject)
This is good. You enable the use of pipeline and every object is sent out one by one when they're created. Export-CSV can then recieve each object and convert them to csv-format.
In your first example using > the output is run through Powershell formatting system while in the second using export-csv it is not.
If you look at get-custompsobject | gm you should see those extra properties that aren't shown in console or sent to your text file.
For export-csv you can control which properties are sent to the csv file using select-object
get-custompsobjct | select-object column1, column2 | export-csv a.csv