beginner Powershell Script Error - powershell

To load a text file of 3 ip addresses comma separated values, into an array, and then have the contents in the array changed for every 3rd octet of the ip address, and then exported back to a csv or text file.
##load file to an array
$ipFileName="C:\Users\HarmanGrewal\Google Drive\win213\assignment2\IP.txt"
$array1=#()
$array1=Get-Content $ipFileName -Delimiter ","
#now we have the contents in an array
$count=0
foreach($i in $array1){
$array1[$count] = $array1[$count] -replace "\.\d{1}\.",".2."
$count++
}
Get-Content $array1 | export-csv -path "C:\Users\HarmanGrewal\test.txt"
A successful exportation of data to a csv file.
An empty csv file instead.
Get-Content : Cannot find path 'C:\Users\HarmanGrewal\192.168.2.10,' because it does not exist.
At C:\Users\HarmanGrewal\Google Drive\win213\assignment2\assignment2QuestionAnswer1.ps1:13 char:1
+ Get-Content $array1 | export-csv -path "C:\Users\HarmanGrewal\test.tx …
+ ~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (C:\Users\HarmanGrewal\192.168.2.10,:String) [Get-Content], ItemNotFound Exception
+ FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetContentCommand
Get-Content : Cannot find path 'C:\Users\HarmanGrewal\192.168.2.14,' because it does not exist.
At C:\Users\HarmanGrewal\Google Drive\win213\assignment2\assignment2QuestionAnswer1.ps1:13 char:1
+ Get-Content $array1 | export-csv -path "C:\Users\HarmanGrewal\test.tx …
+ ~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (C:\Users\HarmanGrewal\192.168.2.14,:String) [Get-Content], ItemNotFound Exception
+ FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetContentCommand
Get-Content : Illegal characters in path.
At C:\Users\HarmanGrewal\Google Drive\win213\assignment2\assignment2QuestionAnswer1.ps1:13 char:1
+ Get-Content $array1 | export-csv -path "C:\Users\HarmanGrewal\test.tx …
+ ~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (C:\Users\HarmanGrewal\192.168.2.15:String) [Get-Content], ArgumentException
+ FullyQualifiedErrorId : ItemExistsArgumentError,Microsoft.PowerShell.Commands.GetContentCommand
Get-Content : Cannot find path 'C:\Users\HarmanGrewal\192.168.2.15' because it does not exist.
At C:\Users\HarmanGrewal\Google Drive\win213\assignment2\assignment2QuestionAnswer1.ps1:13 char:1
+ Get-Content $array1 | export-csv -path "C:\Users\HarmanGrewal\test.tx …
+ ~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (C:\Users\HarmanGrewal\192.168.2.15:String) [Get-Content], ItemNotFoundException
+ FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetContentCommand

# Read the 3 IP addresses from the input file into an array.
# .TrimEnd() ensures that a trailing newline, if any, is stripped (syntax requires PSv3+)
$ips = (Get-Content $ipFileName -Delimiter ",").TrimEnd()
# Replace single-digit IP octets with fixed value 2,
# join the resulting IPs with ',' again, and write to an output file.
$ips -replace '\.\d{1}\.', '.2.' -join ',' | Set-Content "C:\Users\HarmanGrewal\test.txt"
As for what you tried:
$array1=#()
$array1 = ...
$array1=#() is pointless, because the next line assigns to $array1 again, which means that its RHS determines the data type of $array1, irrespective of the previous =#() assignment;
if the Get-Content command happens to return a single value, $array1 will be a scalar; you could prevent that by enclosing the Get-Content command in #(...), the array-subexpression operator, but in PSv3+ that is generally not necessary, due to its unified handling of scalars and collections.
foreach($i in $array1) enumerates the array elements themselves, where $i is a by-value copy of each array element.
Instead of using a separate $count variable to access the elements by reference via their index in order to update them, PowerShell allows you to simply recreate the array as a whole:
$array1 = foreach ($el in $array1) { $el -replace "\.\d{1}\.",".2." }
or, more concisely, relying on the -replace operator's support for array-valued LHS values:
$array1 = $array1 -replace "\.\d{1}\.",".2."
As Harsh Jaswal's answer points out, Get-Content $array1 mistakenly passes intended file contents, whereas what Get-Content expects are filename arguments to read contents from.
Since the values to output - in array $array1 - are already in memory, you can simply send them through the pipeline directly.
Export-Csv operates on the properties of an object, and since you're supplying string objects that only have a .Length property, all that will be exported is that property, which is not the intent.
In the case at hand you have to write a text file directly, using Set-Content, based on constructing CSV-format strings in memory.
Note that Set-Content uses the system's legacy "ANSI" code page by default; use -Encoding <encodingName> to change that.

The first thing is if you are using foreach here then there is no need of $count variable. And the second thing is in your last line you are passing collection $array1 to Get-Content. It takes the path as a parameter. So it is trying to get the contents of the file stored at $array1, which is not correct.
Please modify the code as below.
$ipFileName="C:\Users\HarmanGrewal\Google Drive\win213\assignment2\IP.txt"
$array1=#()
$array1=Get-Content $ipFileName -Delimiter ","
#now we have the contents in an array
foreach($i in $array1){
$i = $i -replace "\.\d{1}\.",".2."
$i
}
$array1 | export-csv -path C:\Users\HarmanGrewal\test.txt

Related

how to append output to a CSV file

foreach ( $newfile in $file )
{
$b = Get-CMDeploymentStatus -PackageId $newfile -StatusType Any | select PackageID
Write-Output $b | Export-Csv -Path "C:\Users\PSM-6A1A000000000000\Documents\list.csv"
}
I am giving input to this with an input file which has number of package names listed and then I want to process it in such a way that the output comes one after the other right now I am getting an error as
Export-Csv : Cannot bind argument to parameter 'InputObject' because it is null. At line:16 char:20 + Write-Output $b | Export-Csv -Path "C:\Users\PSM-6A1A000000000000\Documents\lis ... + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : InvalidData: (:) [Export-Csv], ParameterBindingValidationException + FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.ExportCsvCommand
Your code is assuming that you will have a result coming back from $b, if it does not though, you'll get an error because you're piping $b, which is null, into Export-CSV.
$null |export-csv c:\temp\1.csv
Export-Csv : Cannot bind argument to parameter 'InputObject' because it is null.
At line:1 char:8
+ $null |export-csv c:\temp\1.csv
+ ~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidData: (:) [Export-Csv], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.ExportCs
You should add a 'Guard Clause' before you try to export.
if ($null -ne $b){
Export-csv -Append -InputObject $b
}
At least this will continue executing. Now your next problem is to determine why $b would be empty...from my experience with CM, I bet you need to specify which property in your $file you need. Maybe that line should read:
$b = Get-CMDeploymentStatus -PackageId $newfile.PackageId -StatusType Any | select PackageID
Since you say "I am giving input to this with an input file which has number of package names listed", but your code uses PackageId..
It looks to me that this file contains a packageId, each on a single line.
Anyway, I don't see the code ever reading this file..
If my assumption about the text file is correct, try:
# read the content of the text file and loop through the lines
# collect the output from Get-CMDeploymentStatus in variable $result
$result = Get-Content -Path 'X:\TheFileWithPackageIds.txt' | ForEach-Object {
# inside the ForEach-Object, the $_ automatic variable represents a single line from the text file
Get-CMDeploymentStatus -PackageId $_ -StatusType Any | select PackageID
}
# output on screen
$result
# write to new CSV file
$result | Export-Csv -Path "C:\Users\PSM-6A1A000000000000\Documents\list.csv" -NoTypeInformation

I don't understand using get-content on a csv

How come when I do (Get-Content $CSV1)[2] in the Power-shell console on any .csv file I get the field but when I do it in the ISE I get errors like this.
$CSV1 = Import-Csv "C:\Users\Administrator\Documents\lab8\usersF.csv"
(Get-Content $CSV1)[2]
usersF.csv contents
GivenName
Ylnum
Dexcd
Igbos
Rzjlr
Errors
Cannot index into a null array.
At C:\Users\Administrator\Documents\lab8\ovie_lab7_merged.ps1:5 char:1
+ (Get-Content $CSV1)[2]
+ ~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : NullArray
I've tried the solution #TheIncorrigible1 gave me and it worked, but now I'd like to export that data to a .csv and instead of the string I got the string length in the .csv file how can I fix this?
$CSV1 = Import-Csv -Path "C:\Users\Administrator\Documents\lab8\usersF.csv"
$test = $CSV1.GivenName[4]
$test | Export-Csv "C:\Users\Administrator\Documents\lab8\test.csv"
#TYPE System.String
Length
5
So the problem you're running into is a simple type understanding problem.
Import-Csv takes the Path you provide to it and imports that into a PSCustomObject. You're trying to Get-Content -Path [PSCustomObject], but that is not a supported type for the -Path argument (if you read the help topic, it expects a [String[]]).
If you want that first line, you should do something like:
$Path = 'C:\Users\Administrator\Documents\lab8\usersF.csv'
#(Get-Content -Path $Path)[1] #Arrays are zero-indexed
Alternatively,
$Csv = Import-Csv -Path $Path
$Csv.GivenName[0]
To address your edit, you aren't calling Export-Csv properly. It expects a PSCustomObject, but you're passing it a single string. I'd suggest using Out-File instead.
$CSV1 = Import-Csv -Path C:\Users\Administrator\Documents\lab8\usersF.csv
$test = $CSV1.GivenName[4]
$test |
OutFile -FilePath C:\Users\Administrator\Documents\lab8\test.csv

Search for a string in a number of files, and then rename the files with that string

I'm trying to create a PowerShell script that will search through a series of .txt files (.hl7 files to be exact, but they are just txt files) and search within those files to see if they contain a four digit number. If the file does contain that four digit number, it should then rename the file with the string added to the front of the original file name. So test.hl7 should become 8000_test.hl7 if that file includes those 4 digits within it.
After a day of ferocious googling and digging through this website, this is the best I could muster:
$AccountIDs = ("8155", "8156", "8428")
$Path = "C:\Users\ThatsMe\Downloads\messages"
$Files = (Get-ChildItem $Path -Filter "*.hl7")
for ($i = 0; $i -le $Files.Length; $i++) {
if (Get-Content $Files[$i].FullName | Select-String -Pattern $AccountIDs[$i]) {
Rename-Item $Files[$i].FullName -NewName (($AccountIDs[$i]) + "_" + $Files[$i].Name)
}
}
I am getting some interesting results. I currently have four test files in that messages folder, test, test2, test3, and skibbidybop. The very first one, test gets correctly changed to 8156_test. However, the other files aren't touched. Now, when I change the filename of test to ttest, the script completely skips over that file and then renames test2 and test3 to 8156_test2 (which is incorrect) and 8428_test3 respectively. skibbidybop is never touched.
And, of course, the error message from PowerShell:
Select-String : Cannot bind argument to parameter 'Pattern' because it is null.
At line:6 char:61
+ if (Get-Content $Files[$i].FullName | Select-String -Pattern <<<< $AccountIDs[$i]) {
+ CategoryInfo : InvalidData: (:) [Select-String], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.SelectStringCommand
Get-Content : Cannot bind argument to parameter 'Path' because it is null.
At line:6 char:16
+ if (Get-Content <<<< $Files[$i].FullName | Select-String -Pattern $AccountIDs[$i]) {
+ CategoryInfo : InvalidData: (:) [Get-Content], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.GetContentCommand
Updated Code
$Path = "C:\Users\ThatsMe\Downloads\messages"
$pattern = '\b(8155|8156|8428)\b'
Get-ChildItem $Path -Filter '*.hl7' |
Select-String -Pattern $pattern |
Group-Object Path |
ForEach-Object {
$id = $_.Group.Matches[0].Groups[0].Value
$filename = $_.Group.Filename | Select-Object -First 1
Rename-Item -Path $_.Name -NewName "${id}_${filename}" -WhatIf
}
This is the error that I receive now:
C:\> C:\Users\ThatsMe\Downloads\messages\changename.ps1
Cannot index into a null array.
At C:\Users\ThatsMe\Downloads\messages\changename.ps1:8 char:38
+ $id = $_.Group.Matches[ <<<< 0].Groups[0].Value
+ CategoryInfo : InvalidOperation: (0:Int32) [], RuntimeException
+ FullyQualifiedErrorId : NullArray
What if: Performing operation "Rename File" on Target "Item:
C:\Users\ThatsMe\Downloads\messages\test.hl7 Destination:
C:\Users\ThatsMe\Downloads\messages\_".
Cannot index into a null array.
At C:\Users\ThatsMe\Downloads\messages\changename.ps1:8 char:38
+ $id = $_.Group.Matches[ <<<< 0].Groups[0].Value
+ CategoryInfo : InvalidOperation: (0:Int32) [], RuntimeException
+ FullyQualifiedErrorId : NullArray
What if: Performing operation "Rename File" on Target "Item:
C:\Users\ThatsMe\Downloads\messages\test3.hl7 Destination:
C:\Users\ThatsMe\Downloads\messages\_".
The errors you get are caused by two mistakes, one of them a classic off-by-one error. PowerShell arrays are zero-based, meaning that the last index of the array is one less than the number of its elements:
[ 'a', 'b', 'c' ] → count == 3
0 1 2 → last index == 2 == 3-1
Thus your for loop may run while $i is less than $Files.Length (-lt), not less or equal (-le):
for ($i = 0; $i -lt $Files.Length; $i++) {
Also, you cannot use the same index variable for two different arrays ($Files and $AccountIDs) unless you made sure both arrays have the same length or at least that the second array ($AccountIDs) has more elements than the one used to determine the maximum index ($Files). If $AccountIDs has less elements than $Files your code will eventually attempt to access an index beyond the upper boundary of $AccountIDs. Besides, you probably want to check each file for all of the numbers from $AccountIDs anyway. Doing that requires a nested loop with a second index variable.
With that said, you're making this more complicated than it needs to be. You can simply put your IDs in a single regular expression and pipe the list of files into Select-String to check them against that regular expression:
$pattern = '\b(8155|8156|8428)\b'
Get-ChildItem $Path -Filter '*.hl7' |
Select-String -Pattern $pattern |
Group-Object Path |
ForEach-Object {
$id = $_.Group.Matches[0].Groups[0].Value
$filename = $_.Group.Filename | Select-Object -First 1
Rename-Item -Path $_.Name -NewName "${id}_${filename}" -WhatIf
}
The regular expression \b(8155|8156|8428)\b matches any of the given numbers. The \b restrict the match to word boundaries to avoid matching numbers like 81552 or 842893 as well.
The Group-Object statement ensures the uniqueness of the renamed files (so that you don't attempt to rename a file more than once if more than one match is found in it).
.Matches[0].Groups[0].Value extracts the value of the first capturing group of the first match for each file.
The Select-Object -First 1 ensures that even if multiple matches were found in a file you have just one string with the filename, not an array of them.
Remove the -WhatIf switch once you verified that the rename operation would work correctly and re-run the whole statement to actually rename the files.
Edit: For PowerShell v2 you need to adjust the group handling a little bit, because that version doesn't support member enumeration.
Get-ChildItem $Path -Filter '*.hl7' |
Select-String -Pattern $pattern |
Group-Object Path |
ForEach-Object {
$id = $_.Group | Select-Object -Expand Matches -First 1 |
ForEach-Object { $_.Groups[0].Value }
$filename = $_.Group | Select-Object -Expand Filename -First 1
Rename-Item -Path $_.Name -NewName "${id}_${filename}" -WhatIf
}

Powershell parsing a properties file that contains colons

If I have a .properties file that contains directories (which contain colons):
some_dir=f:\some\dir\etc
another_dir=d:\dir\some\bin
and then use ConvertFrom-StringData to convert Key=Value pairs from said properties file to a hash table:
$props_file = Get-Content "F:\dir\etc\props.properties"
$props = ConvertFrom-StringData ($props_file)
$the_dir = $props.'some_dir'
Write-Host $the_dir
Powershell throws an error (doesn't like colons):
ConvertFrom-StringData : Cannot convert 'System.Object[]' to the type 'System.String' required by parameter 'StringData'. Specified method is not supported.
At line:3 char:32
+ $props = ConvertFrom-StringData <<<< ($props_file)
+ CategoryInfo : InvalidArgument: (:) [ConvertFrom-StringData], ParameterBindingException
+ FullyQualifiedErrorId : CannotConvertArgument,Microsoft.PowerShell.Commands.ConvertFromStringDataCommand
How do you get round this? I'd like to be able to just refer to the directories using the . notation:
$props.'some_dir'
Colons have nothing to do with the error you get. And yes, it can be achieved using ConvertFrom-StringData but, as already mentioned, you're feeding it an array instead of a string. Moreover, you need paths with double backslashes in your file because single backslashes are interpreted as escape characters.
Here's how to fix your code:
# Reading file as a single string:
$sRawString = Get-Content "F:\dir\etc\props.properties" | Out-String
# The following line of code makes no sense at first glance
# but it's only because the first '\\' is a regex pattern and the second isn't. )
$sStringToConvert = $sRawString -replace '\\', '\\'
# And now conversion works.
$htProperties = ConvertFrom-StringData $sStringToConvert
$the_dir = $htProperties.'some_dir'
Write-Host $the_dir
ConvertFrom-StringData expects a string and you are feeding it an array Get-Content cmdlet. Change $props_file to:
$props_file = (Get-Content "F:\dir\etc\props.properties") | Out-String
and you will get another error:
ConvertFrom-StringData : parsing "f:\some\dir\etc" - Unrecognized escape sequence \s.
You can get around it like so:
$props_file = Get-Content "F:\dir\etc\props.properties"
$props = #{}
$props_file | % {
$s = $_ -split "="
$props.Add($s[0],$s[1])
}
$the_dir = $props.'some_dir'
Write-Host $the_dir
Result:
f:\some\dir\etc
You could parse the file as it's loaded, and populate an empty hashtable line-by-line.
$props = #{}
GC "F:\dir\etc\props.properties" | %{$props.add($_.split('=')[0],$_.split('=')[1])}

Path variable does not seem to be consistent in a single Powershell script

I'm running what I think is a relatively simple script:
$txtPath = "c:\users\xxxxxx\desktop\cgc\tx\"
$srcfiles = Get-ChildItem $txtPath -filter "*.txt*"
ForEach($txtfile in $srcfiles) {
Write-Host $txtfile
Get-Content $txtfile
}
and I get the following output:
Automatic_Post-Call_Survey_-_BC,_CC.txt
Get-Content : Cannot find path 'C:\users\x46332\desktop\cgc\Automatic_Post-Call_Survey_-_BC,_CC.txt' because it does no
t exist.
At C:\users\x46332\desktop\cgc\testcount2.ps1:34 char:13
+ Get-Content <<<< $txtfile
+ CategoryInfo : ObjectNotFound: (C:\users\x46332...ey_-_BC,_CC.txt:String) [Get-Content], ItemNotFoundEx
ception
+ FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetContentCommand
This is the output from Write-Host $txtfile followed immediately by Get-Content $txtfile and get-content seems to be the heart of my issue.
When I comment out the Get-Content line, the script generates a list of the filenames to the console. This suggests to me that the $txtPath is properly defined. However, I add Get-Content for the SAME file/same variable and for some reason, the \tx portion of the path disappears from the search string. My filename prints, but then Get-Content can't find the path for the filename is just printed.
I suspect that the "directory doesn't exist" error isn't really that the directory doesn't exist. So what should I be looking at? There's not a lot of space in my code for an error to hide, but I can't find it...thoughts?
Get-Content needs the full path e.g.:
Get-Content $txtFile.FullName
When you specify Get-Content $txtFile, PowerShell attempts to coerce the argument $txtFile to the required argument Path and to do so, it coerces the FileInfo object to a string. This process yields just the name of the file.
Another way to do this is:
$txtFile | Get-Content