Can't get it to provide proper output - function

I have this function and I can not get it to work, the $DecimalConversion output is not coming out.. I think I am having some syntax errors.
function Get-DecimalNumber(){
$FileCheck = Test-Path "C:\Conversions\conversions.csv"
if($FileCheck){
Do
{
[int]$GetDecimal = Read-host "Write a number between 1-255" | Out-Null
}
while ($GetDecimal -notmatch '\d{1,3}' -or (1..255) -notcontains $GetDecimal)
$DecimalConversion= "{0:X}" -f $GetDecimal
$DecimalConversion
}
else{Write-Warning "Can not find conversions.csv, creating now under C:\Conversions\"; New-Item "C:\Conversions\conversions.csv" -Force | Out-Null}
}
$getfunction=Get-DecimalNumber

You could probably use a better while condition. However, ur issue is caused because of the out-null cmdlet on read-host.
If you use that, $GetDecimal will not get the value you pass in since the out-null is processed before the assignment happens. Just remove it. And it should work.

Final code, I think this looks better, let me know what you think!
function Get-DecimalNumber {
<#
.Description
The Get-DecimalNumber function gets user input for a decimal number and
converts it into hexadecimal and binary numbers. Then this data is added to an
excel file (.csv) and the date of conversion is displayed in short form m/d/yyyy.
#>
$ErrorActionPreference = 'silentlycontinue' #Silences errors
$Test = Test-Path "C:\temp\test\conversions.csv" #Variable to test path
if (! $Test) { #Checking if path does not exist
Write-Warning "conversions.csv File Not Present, creating under C:\temp\test\"
New-Item 'C:\temp\test\conversions.csv' -Force | Out-Null; break; exit #Creating path with file & suppressing output
}
else {
[int]$Num = Read-Host "Enter number from 1-255"
if ($Num -gt 255 -or $Num -le 1) {
Write-Warning "You did not enter a number in the specified range"; break; exit
}
else {
$Hex = [Convert]::ToString($Num, 16) #Converting from decimal to hexadecimal
$Bin = [Convert]::ToString($Num, 2) #Converting from decimal to binary
Write-Host "Decimal to Hex and Binary:"
$NewHashTable1 = #{ } #Creating hashtable
$NewHashTable1.Add('Decimal', $Num) #Adding values from variables to hash table
$NewHashTable1.Add('Hexadecimal', $hex)
$NewHashTable1.Add('Binary', $bin)
$NewHashTable1 #Output to screen the previously created hashtable
$NewHashTable1 >> "C:\temp\test\conversions.csv" #Appending hashtable to .csv file
Write-Output "'n"
Get-Date -Format d #Output date in short format
$Now = Get-Date -Format d
$Now >> "C:\temp\test\conversions.csv" #Output date to .csv file
}
}
}

Related

New-Item messing up my variable PowerShell [duplicate]

This question already has an answer here:
Powershell Join-Path showing 2 dirs in result instead of 1 - accidental script/function output
(1 answer)
Closed 1 year ago.
I wrote a very simple script to acquire a random free drive letter.
The function finds a random free letter , creates a new empty text file of that drive letter name eg. Q.txt
I then return the value as $new_letter but when it comes out of the function somehow newly file created path is a part of the variable C:\AppPack\Logs\Q.txt Q
Is it something New-Item messing up with my $new_letter variable ?
function get_drive_letter()
{
$letter_acquired = $false
Do
{
$new_letter = Get-ChildItem function:[h-z]: -Name | ForEach-Object { if (!(Test-Path $_)){$_} } | random -Count 1 | ForEach-Object {$_ -replace ':$', ''}
write-host ("RIGHT AFTER " + $new_letter)
if (!(test-path "C:\AppPack\Logs\$new_letter.txt"))
{
New-Item -Path C:\AppPack\Logs\ -Name "$new_letter.txt" -ItemType "file"
write-host ("FROM FUNCTION " + $new_letter)
$letter_acquired = $true
return $new_letter
}
else
{
write-host ("LETTER USED ALREADY")
write-host ($new_letter)
}
}
while($letter_acquired = $false)
}
$drive_letter = $null
$drive_letter = get_drive_letter
write-host ("RIGHT AFTER FUNCTION " + $drive_letter)
OUTPUT :
RIGHT AFTER Q
FROM FUNCTION Q
RIGHT AFTER FUNCTION C:\AppPack\Logs\Q.txt Q
A PowerShell function outputs everything, not just the result of the expression right after return!
The additional file path you see is the output from New-Item ... - it returns a FileInfo object for the file you just created.
You can suppress output by assigning it to the special $null variable:
# Output from New-Item will no longer "bubble up" to the caller
$null = New-Item -Path C:\AppPack\Logs\ -Name "$new_letter.txt" -ItemType "file"
return $new_letter
Or by piping to Out-Null:
New-Item ... |Out-Null
Or by casting the entire pipeline to [void]:
[void](New-Item ...)
Although I recommend explicitly handling unwanted output at the call site, you can also work around this behavior with a hoisting trick.
To demonstrate, consider this dummy function - let's say we "inherit" it from a colleague who didn't always write the most intuitive code:
function Get-RandomSquare {
"unwanted noise"
$randomValue = 1..100 |Get-Random
"more noise"
$square = $randomValue * $randomValue
return $square
}
The function above will output 3 objects - the two garbage strings one-by-one, followed by the result that we're actually interested in:
PS ~> $result = Get-RandomSquare
PS ~> $result
unwanted noise
more noise
6400
Let's say we've been told to make as few modifications as possible, but we really need to suppress the garbage output.
To do so, nest the entire function body in a new scriptblock literal, and then invoke the whole block using the dot-source operator (.) - this forces PowerShell to execute it in the function's local scope, meaning any variable assignments persist:
function Get-RandomSquare {
# suppress all pipeline output
$null = . {
"unwanted noise"
$randomValue = 1..100 |Get-Random
"more noise"
$square = $randomValue
return $square
}
# variables assigned in the block are still available
return $square
}

How to create a Function using Powershell

I need help with the code below. I want the script to perform the following: prompt the user for an AD group name, if the group name is found, export the group members to a CSV file. one of the requirements is that I must include a function statement. Thank you in advance.
The code works if I use a variable like the following example: $groupsusers = Get-ADGroup -Identity $nameofgroup, instead of the function statement.
However, I don't want to use a variable, I want to implement a function statement.
$prompt = "Enter A Group Name"
do
{
$nameofgroup = Read-Host $prompt
}
until(!$(dsquery Group-Object $nameofgroup; $prompt = "Group
'$nameofgroup' was not found, try again"))
$nameofgroup = Read-Host $prompt
function GetGroupInfoToCsv (#what parameters go here?){
ForEach-Object{
$settings = #{ Group = $_.DistinguishedName; Member = $null }
$_| Get-ADGroupMember |
ForEach-Object{
$settings.Member = $_.DistinguishedName
New-Object PsObject -Property $settings
}
}
}
GetGroupInfoToCsv | Export-Csv .\GroupMembers.csv -NoTypeInformation
You could combine the asking for user input and returning the info into the same function. Something like this:
function Get-GroupMembers {
$prompt = "Enter A Group Name. Press Q to quit"
# create an endless loop
while ($true) {
Clear-Host
$answer = Read-Host $prompt
# if the user has had enough, exit the function
if ($answer -eq 'Q') { return }
# try and find one or more AD groups using the answer as (part of) the name
$group = Get-ADGroup -Filter "Name -like '*$answer*'"
# if we have found something, exit the while loop and start enumerating the members
if ($group) { break }
$prompt = "Group '$answer' was not found, try again. Press Q to quit"
}
# you only get here if Get-ADGroup found one or more groups
$group | ForEach-Object {
# output a PSObject with the properties you are after
$members = $_ | Get-ADGroupMember
foreach ($member in $members) {
[PsCustomObject]#{
'Group' = $_.DistinguishedName
'Member' = $member.DistinguishedName
}
}
}
}
# call the function
$groupinfo = Get-GroupMembers
# test if the function returned anything.
# the user could have cancelled of the group had no members to output
if ($groupinfo) {
Write-Host "Adding $($groupinfo.Count) items to the CSV file"
# without -Append, you would overwrite your CSV file..
$groupinfo | Export-Csv .\GroupMembers.csv -NoTypeInformation -Append
}
else {
Write-Host 'Nothing found..'
}
As you can see, I have changed the function name so it complies with the Verb-Noun convention in PowerShell.

PowerShell adds other values to return value of function

It seems that PowerShell adds an additional variable to the return value of a function.
The function subfoo2 itself delivers the correct values, but as soon as PowerShell jumps back to the postion where I called the function (in foo1), value contains the value of an other variable ($msg)
(Have a look at the comments in the code)
writeMessageLog($msg){
...
Add-Content $msg
...
}
subfoo2{
writeMessageLog($msg)
return $UserArrayWithValues #During Debug, $Array is fine (1)
}
foo1{
$var = subfoo2 $UserArray # $var has now the value of $msg and $UserArrayWithValues (2)
#do something with var
}
Realcode:
function WriteLog
{
param ( [string] $severity , $msgNumber, [string] $msg )
...
$msgOut = $date + ... + $msg
Add-Content $msgout ( $msgOut )
...
}
function getFeatures
{
writelog 'I' 1002 $true $true "Load Features"
$Features = importCsv -pPath $FeatureDefintionFilePath
Writelog 'I' 1000 $true $true "Features Loaded"
return $Features # $Features has value as expected (1)
}
function GetUserFeatures ($pUserObject)
{
$SfBFeatures = ""
$SfBFeatures = getFeatures #SfBFeaures has Value of $msg and $Features (2)
...
}
Do I use the functions/return values wrong? What could lead to such behavior? Is it an issue if i call a function within a function?
If I remove $msgOut = $date + ... + $msg in writeMessageLog, the values are fine.
I'm pretty lost right now, and have no ideas where this comes from. Any ideas welcome.
This is how powershell works, basically everything that you print out will be returned as the function output. So don't output extra stuff. To force something to not output stuff you can do:
$null = some-command_that_outputs_unwanted_things
since everybody is obsessed with Out-Null I'll add this link showing several other ways to do that.
Within a function, everything you don't assign or pipe to a consuming cmdlet will get put to the pipeline and returned from the function - even if you don't explicit return it. In fact the return keyword doesn't do anything in PowerShell so the following is equivalent:
function Test-Func
{
"Hello World"
}
function Test-Func
{
return "Hello World"
}
So it looks like your writeMessageLog puts anything on the pipeline thus you have to either assign the value to anything:
$notUsed = writeMessageLog($msg)
or (prefered) pipe it to the Out-Null cmdlet:
writeMessageLog($msg) | Out-Null

PowerShell Error: Import-CSV Cannot Open File

My PowerShell code is tripping some strange error codes, which I have thoroughly searched for to no avail.
I am trying to create a script that calculates some basic statistics for a number of CSV files, which seems to work until I try to create and populate new columns.
The error code I'm getting is:
Import-Csv: Cannot open file "C:\zMFM\Microsoft.Powershell.Commands.GenericMeasureInfo".
At C:\zMFM\StatsDebug.ps1:46 Char:21
+$STATS2 = Import-CSV <<< $STATS
+CategoryInfo : OpenError (:) [Import-Csv], FileNotFoundException
+FullyQualifiedErrorId : FileOpenFailure.Microsoft.Powershell.Commands.ImportCsvCommand at C:\zMFM\statsdebug.ps1:55 char:20
That's followed by an error using a null expression, but I assume fixing this problem with Import-Csv will in turn fix that error. Here's my code, any help would be great, thanks!
$i = 1
#$colSTDDEV = New-Object System.Data.DataColumn StdDev,([double])
$colVZA = New-Object System.Data.DataColumn VZA,([double])
#$colVAZ = New-Object System.Data.DataColumn VAZ,([double])
While ($i -le 211) {
#Set the variable to the filename with the iteration number
$filename = "c:\zMFM\z550Output\20dSummer\fixed20dSum550Output$i.csv"
#Check to see if that a file with $filename exists. If not, skip to the next iteration of $i. If so, run the code to collect the
statistics for each variable and output them each to a different file
If (Test-Path $filename) {
#Calculate the Standard Deviation
#First get the average of the values in the column
$STDEVInputFile = Import-CSV $filename
#Find the average and count for column 'td'
$STDEVAVG = $STDEVInputFile | Measure-Object td -Average | Select Count, Average
$DevMath = 0
# Sum the squares of the differences between the mean and each value in the array
Foreach ($Y in $STDEVInputFile) {
$DevMath += [math]::pow(($Y.Average - $STDEVAVG.Average), 2)
#Divide by the number of samples minus one
$STDEV = [Math]::sqrt($DevMath / ($STDEVAVG.Count-1))
}
#Calculate the basic statistics for column 'td' with the MEASURE-OBJECT cmdlet
$STATS = Import-CSV $Filename |
Measure-Object td -ave -max -min
#Append the standard deviation variable to the statistics array and add the value
$STATS2 = Import-CSV $Stats
$VZA = $filename.VZA
#$VAZ = $filename.VAZ #COMMENTED FOR DEBUGING
#$STATS2.Columns.Add($colSTDDEV) #COMMENTED FOR DEBUGING
#$STATS2[0].StdDev = $STDEV #COMMENTED FOR DEBUGING
$STATS2.Columns.Add($colVZA)
$STATS2[0].VZA = $VZA
#$STATS2.Columns.Add($colVAZ) #COMMENTED FOR DEBUGING
#$STATS2[0].VZA = $VAZ #COMMENTED FOR DEBUGGING
#Export the $STATS file containing everything you need in the correct folder
Export-CSV -notype "c:\zMFM\z550Output\20dSummer\20dSum550Statistics.csv"
}
$i++
}
$STDEVInputFile = Import-CSV $filename
...
$STATS = Import-CSV $Filename |
Measure-Object td -ave -max -min
# $STATS here will be type [GenericMeasureInfo], so you cannot use this as a filename.
$STATS2 = Import-CSV $Stats
# Are you trying to import $filename a third time here? You can't use $Stats because it's a [GenericMeasureInfo] object, not a [string].
Based on your code, it looks like you're trying to import a CSV with a filename of type [Microsoft.PowerShell.Commands.GenericMeasureInfo]. Filenames are strings. Also, why are you importing $filename twice? Would recommend importing it once, then operating off of the variable you saved it to.
Would also recommend changing your while loop to 1..211 | ForEach-Object. That way you won't have to worry about whether $i is less than or equal to your static number. Not a huge deal, but would make the code a little more readable.
TL;DR
Import-CSV $Stats is the problem. $Stats is not a valid filename, so you can't open/import it.

Newly Created Column is Null.... Why?

I'm trying to do a simple task in PowerShell where some basic statistics are calculated for a number of columns in a CSV file. I'm nearly done, but I keep getting an error where new columns I create are coming up as being Null. I cannot see where I am going wrong, here.
Specifically, the line of code causing the error is
$STATS2.Columns.Add($colVZA) |
The tables created when importing $filename do have columns named VZA, VAZ, etc., so that's not the problem.
It seems like adding and populating columns should be a simple task, so I'm sure I'm missing something simple here. Here's my code:
#######################
function Get-Type
{
param($type)
$types = #(
'System.Boolean',
'System.Byte[]',
'System.Byte',
'System.Char',
'System.Datetime',
'System.Decimal',
'System.Double',
'System.Guid',
'System.Int16',
'System.Int32',
'System.Int64',
'System.Single',
'System.UInt16',
'System.UInt32',
'System.UInt64')
if ( $types -contains $type ) {
Write-Output "$type"
}
else {
Write-Output 'System.String'
}
} #Get-Type
#######################
<#
.SYNOPSIS
Creates a DataTable for an object
.DESCRIPTION
Creates a DataTable based on an objects properties.
.INPUTS
Object
Any object can be piped to Out-DataTable
.OUTPUTS
System.Data.DataTable
.EXAMPLE
$dt = Get-psdrive| Out-DataTable
This example creates a DataTable from the properties of Get-psdrive and assigns output to $dt variable
.NOTES
Adapted from script by Marc van Orsouw see link
Version History
v1.0 - Chad Miller - Initial Release
v1.1 - Chad Miller - Fixed Issue with Properties
v1.2 - Chad Miller - Added setting column datatype by property as suggested by emp0
v1.3 - Chad Miller - Corrected issue with setting datatype on empty properties
v1.4 - Chad Miller - Corrected issue with DBNull
v1.5 - Chad Miller - Updated example
v1.6 - Chad Miller - Added column datatype logic with default to string
v1.7 - Chad Miller - Fixed issue with IsArray
.LINK
http://thepowershellguy.com/blogs/posh/archive/2007/01/21/powershell-gui-scripblock-monitor-script.aspx
#>
function Out-DataTable
{
[CmdletBinding()]
param([Parameter(Position=0, Mandatory=$true, ValueFromPipeline = $true)] [PSObject[]]$InputObject)
Begin
{
$dt = new-object Data.datatable
$First = $true
}
Process
{
foreach ($object in $InputObject)
{
$DR = $DT.NewRow()
foreach($property in $object.PsObject.get_properties())
{
if ($first)
{
$Col = new-object Data.DataColumn
$Col.ColumnName = $property.Name.ToString()
if ($property.value)
{
if ($property.value -isnot [System.DBNull]) {
$Col.DataType = [System.Type]::GetType("$(Get-Type $property.TypeNameOfValue)")
}
}
$DT.Columns.Add($Col)
}
if ($property.Gettype().IsArray) {
$DR.Item($property.Name) =$property.value | ConvertTo-XML -AS String -NoTypeInformation -Depth 1
}
else {
$DR.Item($property.Name) = $property.value
}
}
$DT.Rows.Add($DR)
$First = $false
}
}
End
{
Write-Output #(,($dt))
}
$i = 1
While ($i -le 211) {
#Set the variable to the filename with the iteration number
$filename = "c:\zMFM\z550Output\20dSummer\fixed20dSum550Output$i.csv"
#Check to see if that a file with $filename exists. If not, skip to the next iteration of $i. If so, run the code to collect the statistics for each variable and output them each to a different file
If (Test-Path $filename) {
#Calculate the Standard Deviation
#First get the average of the values in the column
$STDEVInputFile = Import-CSV $filename
#Find the average and count for column 'td'
$STDEVAVG = $STDEVInputFile | Measure-Object td -Average | Select Count, Average
$DevMath = 0
# Sum the squares of the differences between the mean and each value in the array
Foreach ($Y in $STDEVInputFile) {
$DevMath += [math]::pow(($Y.Average - $STDEVAVG.Average), 2)
#Divide by the number of samples minus one
$STDEV = [Math]::sqrt($DevMath / ($STDEVAVG.Count-1))
}
#Calculate the basic statistics for column 'td' with the MEASURE-OBJECT cmdlet
$STATS = Import-CSV $Filename |
Measure-Object td -ave -max -min |
#Export the statistics as a CSV
Export-CSV -notype "c:\zMFM\z550Output\20dSummer\tempstats$i.csv"
$GetColumns = Import-CSV $filename
#Append the standard deviation variable to the statistics table and add the value
$STATS2 = Import-CSV "c:\zMFM\z550Output\20dSummer\tempstats$i.csv"
$StatsTable = Get-PSDrive | Out-DataTable
#$colSTDDEV = New-Object System.Data.DataColumn StdDev,([double])
$colVZA = New-Object System.Data.DataColumn VZA,([double])
#$colVAZ = New-Object System.Data.DataColumn VAZ,([double])
$colVZA = $GetColumns[0].VZA
#$colVAZ = $GetColumns[0].VAZ #COMMENTED FOR DEBUGGING
#$colSTDDEV = $STDEV
#$StatsTable.Columns.Add($colSTDDEV) #COMMENTED FOR DEBUGGING
#$StatsTable[0].StdDev = $STDEV #COMMENTED FOR DEBUGGING
$StatsTable.Columns.Add($colVZA) |
#$StatsTable[0].VZA = $VZA
#$StatsTable.Columns.Add($colVAZ) #COMMENTED FOR DEBUGGING
#$StatsTable[0].VZA = $VAZ #COMMENTED FOR DEBUGGING
#Export the $STATS file containing everything you need in the correct folder
Export-CSV -notype "c:\zMFM\z550Output\20dSummer\20dSum550Statistics.csv"
}
$i++
}
Even though each object in $STATS2 have the same properties, the $STATS2 object itself is just a simple array, an unstructured list of objects - it doesn't have a Columns property with an Add() method:
$STATS2.Colums.Add($colVZA)
^ ^ ^
[array] | |
$null |
this fails
You can convert the array you get from Import-Csv from an array to a DataTable object (which has Columns) by inspecting each property in the first object in the array, like in the Out-DataTable sample on the technet script gallery