I have created 3 arrays, which store data from a separate file called household.dat, arr1 holds the ID, arr2 holds the income, and arr3 holds family members. The user is asked to enter an existent id (one that exists in the household file). If the id exists, I have to print the information related to that specific ID. The problem starts when I try to compare the user's input with the already stored id.
This is what I have so far.
$myarr contains the first set of numbers in the first column which is the ID
$myarr1 contains the second set of numbers in the second column, which is the income
$myarr2 contains the third set of numbers in the third column which is the members
10041,12180.00,4
15298,89254.00,3
10562,13240.00,3
13256,19800.00,2
47742,67189.00,4
14830,22458.00,8
19000,17000.00,2
21132,18125.00,7
23541,15623.00,2
82772,56878.00,2
32100,3200.00,6
67733,98113.00,5
36002,6500.00,5
37734,45144.00,4
65410,11970.00,2
47352,8900.00,3
62159,10000.00,2
92803,6200.00,1
"{0,25:n3}" -f "Household Statistics"
write-output "--------------------------------"
write-output "1. Search by Household ID"
write-output "2. List all"
$option = read-host "Enter a option (0 to quit)"
$myarr = #()
$myarr1 = #()
$myarr2 = #()
$id = #()
if($option -eq 1)
{
$id = Read-Host -Prompt "Enter Household ID "
foreach ($line in $file)
{
$line = $line -split (",")
$myarr = $line[0]
$myarr1 = $line[1]
$myarr2 = $line[2]
if ($myarr -contains $id)
{
write-output "--------------------------------"
write-host "Statistics for household" $id
write-output "--------------------------------"
"{0,-11} {1,10} {2,15}" -f "Household ID", "Income",
"Members"
}
elseif ($myarr -notcontains $id)
{
""
write-output "Sorry, entered Household ID not found"
break;
""
}
}
If you're not reading from a file as CSV, but somehow created 3 (same-length) arrays, you can start your code by combining the array values into workable objects like this:
$data = for ($i = 0; $i -lt $myarr.Count; $i++) {
[PsCustomObject]#{
ID = $myarr[$i]
Income = $myarr1[$i]
Members = $myarr2[$i]
}
}
However, reading your question a couple of times over, and looking at the examples you give for the 3 arrays, it seems to me that your file household.dat is in fact a CSV file without headers.
Instead of reading that file with Get-Content and then loop over the lines, manually splitting them on the comma as in your code, you should use Import-Csv, so you will obtain an array of objects where each object has both the 'ID', the 'Income' AND the corresponding 'Members' properties, nicely packed together.
In that case just do:
$data = Import-Csv -Path 'X:\somewhere\household.dat' -Header ID, Income, Members
Then your code would be much simpler like:
# loop over the records in the $data
$id = Read-Host -Prompt "Enter Household ID"
# see if you can find a matching record
$record = $data | Where-Object { $_.ID -eq $id }
if ($record) {
Write-Host "--------------------------------"
Write-Host "Statistics for household $id"
Write-Host "--------------------------------"
"{0,-11} {1,10} {2,15}" -f $record.ID, $record.Income, $record.Members
}
else {
""
Write-Host "Sorry, entered Household ID '$id' not found"
""
}
Related
Here is my issue - I have a Powershell script that calls a bunch of information from an API, that comes in as JSON.
As an example (not the actual output, but good enough for my issue):
{
"fruit":[
{
"Type": "Apple",
"ID": 1
},
{
"Type": "Bannana",
"ID": 2
}
]
}
The API that is called has a search variable that is specified by the user, depending on what they specify, the API could return No results, a single result or multiple results.
What I want to do is to present the user with a list of Fruit, based off of the type field and an option: e.g.:
Press 1 for Apples
Press 2 for Bannanas
Press 0 to enter a new search field
If there are more options then obviously Press X for XXXX until all the options are accounted for.
I suspect I will have to do some form of loop through the JSON list to populate a set of fields, - I've never had an interactive section like this in a PS Script.
So - in the end, this was what I did to fix it - thanks to the commentors who pointed me in the right direction:
$variablearray = #()
$i = 1
foreach($_ in $fruitnames){
New-Variable -Name "$i" -Value $_
$variablearray += $i
$i = $i +1
}
$optionsarray = #()
$optionsarray += $Zero
foreach($_ in $variablearray){
$word = Convert-NumbertoWords $_
$tmp2 = (get-variable -name $_).value
$tmp = echo $word[1].trim()
$tmp3 = New-Object System.Management.Automation.Host.ChoiceDescription "$tmp", "$tmp2"
New-Variable -Name $tmp -Value $tmp3
$optionsarray += $tmp3
}
#Combine the options array to an actual options object
$options = [System.Management.Automation.Host.ChoiceDescription[]]($optionsarray)
#prompt the user for the choice
$title = 'Select the Fruit'
$message = 'Hover over each option to check what the description is'
$result = $host.ui.PromptForChoice($title, $message, $options, 0)
}
It loops through all the JSON Elements, creates a set of variables (this is needed for other parts of the script and for ease of use) and then creates an options array and then prompts the user for input.
Ask user to enter a name, Search name in the names array person.dat file. If the name is found print a table, If the name is not found, print an error message and ask user for another name.
persons.dat.
George Nelson,56,78000.00
Mary Nathaniel,65,66300.00
Rosy Ferreira,32,39000.00
Guessing on this part.
While ($true){
Write-Host $("1. Search by user name")
Write-Host $("2. List all:)
$input = (Read-Host("Enter an option (0 to quit)"))##user will input value
#if 1 is entered (Read-Host("Enter user name"))
#if 2 is entered Print all#
#if 0 is entered quit.#
try{ ? }
catch {
## If input is invalid, restart loop
Write-host " User does not exist"
continue
}
0{
Write-Host $("Thank you. Bye!")
This bottom part will print all 3 in a table.
$data = Get-Content "persons.dat"
$line = $null;
[String[]] $name = #();
[int16[]] $age = #();
[float[]] $salary = #();
foreach ($line in $data)
{ #Split fields into values
$line = $line -split (",")
$name += $line[0];
$age += $line[1];
$salary += $line[2];
}
Write-Host $("{0,-20} {1,7} {2,11}" -f "Name", "Age", "Salary")
Write-Host $("{0,-20} {1,7} {2,11}" -f "-----------", "---", "-----------")
for
($nextItem=0 ; $nextItem -lt $name.length; $nextItem++)
{
$val1n = $name[$nextItem];
$val2n = $age[$nextItem]
$val3n = $salary[$nextItem]
Write-Host $("{0,-20} {1,7} {2,11:n2}" -f $val1n,
$val2n, $val3n)
}
Here is one way you could do it, hopefully the inline comments help you understand the logic. Since the persons.dat file you're showing us is comma-delimited, we can convert it to an object using ConvertFrom-Csv, by doing this, you won't have a need to construct the output to screen like you are doing with those Write-Host statements.
# Convert the file into an object
$persons = Get-Content persons.dat -Raw | ConvertFrom-Csv -Header "Name", "Age", "Salary"
function ShowMenu {
# simple function to clear screen and show menu when called
Clear-Host
'1. Search by user name'
'2. List all'
}
:outer while($true) {
# clear screen and show menu
ShowMenu
while($true) {
# ask user input
$choice = Read-Host 'Enter an option (0 to quit)'
# if input is 0, break the outer loop
if(0 -eq $choice) {
'Goodbye'
break outer
}
# if input is not 1 or 2
if($choice -notmatch '^(1|2)$') {
'Invalid input!'
$null = $host.UI.RawUI.ReadKey()
# restart the inner loop
continue
}
# if we are here user input was correct
break
}
$show = switch($choice) {
1 {
# if choice was 1, ask user for a user name
$user = Read-Host "Enter user name"
# if user name exists in the `$persons` object
if($match = $persons.where{ $_.Name -eq $user }) {
# output this to `$show`
$match
# and exit this switch
continue
}
# if user name was not found
"$user was not found in list."
}
2 {
# if input was 2, output `$persons` to `$show`
$persons
}
}
# show the object to the host
$show | Out-Host
# and wait for the user to press any key
$null = $host.UI.RawUI.ReadKey()
}
So I'm trying to write a script that will generate an AD report based on parameters chosen by the user, and it's caused me a lot more headache than I expected. Here's an excerpt of what I'm trying to do:
#import the ActiveDirectory Module
Import-Module ActiveDirectory
#report parameter variables
$firstname = {givenname}
$lastname = {surname}
$displayname = {DisplayName}
$logonname = {sAMAccountName}
#set array initially to number of possible parameters
$inputarray = (0..3)
#display menu
cls
Write-Host "Please select the parameters you would like in your report:"
Write-Host "Enter q when finished"
Write-Host `n
Write-Host " 1) First Name"
Write-Host " 2) Last Name"
Write-Host " 3) Display Name"
Write-Host " 4) Logon Name"
Write-Host `n
#read in selections from user while input isn't Q, and not bigger than array
#bounds
for ($i=0; (($i -le 3) -and ($inputarray[$i] -ne 'q')); $i++){
$selection = ($i + 1)
$inputarray[$i] = Read-host "Enter report parameter $selection"
#exit loop for quit selection
if ($inputarray[$i] -eq 'q'){
break
}
switch ($inputarray[$i])
{
"1" { $result = $firstname }
"2" { $result = $lastname }
"3" { $result = $displayname }
"4" { $result = $logonname }
}
$inputarray[$i] = $result
}
$arraylen = $i
$test = ''
for ($x=0; $x -lt $arraylen; $x++){
if($x -lt ($arraylen -1)){
$test = ($test + $inputarray[$x] + ',')
}
else{
$test = ($test + $inputarray[$x])
}
}
Get-ADUser -searchbase "my targeted OU" -Properties * -Filter * |
Select-Object $test |
Export-Csv -Path "export path here.csv" -NoTypeInformation
I thought creating a string from the array with comma separated values would work the same as typing them in (like Select-Object givenname,surname,lastlogin) but that clearly isn't working. Any ideas how to change it back from a string value to individual objects, so maybe it will accept it?
1st problem: Statements such as $firstname = {givenname} assign a script block ({...}) to the variable, which is not your intent; instead, you're looking to assign property names as strings, e.g., $firstname = 'givenname'.
2nd problem: You're building variable $test as a string, whereas what you need to pass to Select-Object is an array of strings (property names), so you can just use $inputArray directly.
I have a text file array JobTitle.txt which looks like this:
Sales Co-Worker, TSALES, TSALSK
Business Navigator, BNOM, BNOMD
And I wanted to write a code that would read the user's input and present the second and the third value from the same line. Here's what I wrote:
$jobtitledb = Get-Content C:\Users\Username\Desktop\Scripts\JobTitle.txt
$jobtitleinput = Read-Host 'Input the job title'
foreach ($data in $jobtitledb) {
$jobtitleinput, $basic, $extended = $data -split ','
Write-Host "Basic template is: "$basic
Write-Host "Extended template is: "$extended
}
I can't seem to figure out how to make it return desired line only. For clarification, when I input Sales Co-Worker I want the program to return:
Basic template is: TSALES
Extended template is: TSALSK
You just need an if statement that checks to make sure your input was the same as the jobtitle its reading in on each line.
$jobtitledb = Get-Content C:\Users\Username\Desktop\Scripts\JobTitle.txt
$jobtitleinput = Read-Host 'Input the job title'
foreach($data in $jobtitledb) {
$jobtitle, $basic, $extended = $data -split ','
If ($jobtitle -eq $jobtitleinput) {
Write-host "Basic template is: "$basic
Write-host "Extended template is: "$extended
}
}
Also I think when you were reading each line you were assigning the jobtitle to the same variable as the user input, so you should change that as well. Above code should work.
Here's an annotated script that should fix your problem. It's mostly the same as the original except where I changed it to store the job tile from the record in $jobtitle instead of $jobtitleinput and added an if statement. Also added a $jobnotfound variable and code to print the appropriate message
$jobtitledb = Get-Content C:\Users\Username\Desktop\Scripts\JobTitle.txt
$jobtitleinput = Read-Host 'Input the job title'
$jobnotfound = $ftrue
foreach($data in $jobtitledb)
{
# Store the job title from the record in $jobtitle instead of
# overwriting $inputjobtitle
$jobtitle, $basic, $extended = $data -split ','
# check the $jobtitle from record against the $jobtitleinput
if ($jobtitle -match $jobinputtitle)
{
Write-host "Basic template is: "$basic
Write-host "Extended template is: "$extended
$jobnotfound = $false
break
}
}
if ($jobnotfound)
{
Write-Host "No job matching '$jobinputtitle' was found."
}
I added an "else" statement, else { Write-Host 'Given job title does
not exist' } But it runs once for each line. How to make it return
only 1 line of "Given job title does not exist"?
I can't post comments yet, however you should just be able to use break within your else statement to exit the foreach loop.
---------- Edit ----------
The following should provide the desired output.
$jobtitledb = Get-Content C:\Users\Username\Desktop\Scripts\JobTitle.txt
$jobtitleinput = Read-Host 'Input the job title'
$found = $false
foreach($data in $jobtitledb)
{
$jobtitle, $basic, $extended = $data -split ','
If ($jobtitle -eq $jobtitleinput) {
Write-host "Basic template is: "$basic
Write-host "Extended template is: "$extended
$found = $true
break
}
}
if(!$found)
{
Write-Host "Given job title does not exist"
}
So I have a few arrays with names that I want to search though, I would like to keep the arrays separate as they are each specific to a certain group of names. I'm trying to figure out how to search though more then one at the same time. The code I have below is how to search though one array but I'm not sure the best way to search multiple. I tried to add -and $array2 into the foreach but that did not work.
I know I could just add the same block for each array but I'm wondering if there is a cleaner and more efficient way to do that.
$array1 = "name1", "name2", "name3"
$array2 = "name4", "name5", "name6"
$searchname = Read-Host "Enter the name to search for"
foreach($name in $array1){
if($searchname -eq $name){
Write-Host "$searchname found"
}
}
If you just need to verify whether the name is present in any of the arrays you could simply concatenate them and check if the result contains the name you're looking for:
if (($array1 + $array2) -contains $name) {
Write-Host "$name found"
}
If you want to identify the array in which it was found you could do something like this:
'array1', 'array2' | ForEach-Object {
if ((Get-Variable $_).Value -contains $name) {
Write-Host "$name found in `$$_"
break
}
}
or like this, if the arrays were stored in a hashtable rather than individual variables:
$hash = #{
array1 = "name1", "name2", "name3"
array2 = "name4", "name5", "name6"
}
$hash.GetEnumerator() | ForEach-Object {
if ($_.Value -contains $name) {
Write-Host ('{0} found in ${1}' -f $name, $_.Name)
break
}
}
If you want to search across items in multiple arrays, you can concatenate the arrays in the foreach statement like so:
foreach($name in #($array1;$array2)){
if($searchname -eq $name){
Write-Host "$searchname found"
}
}
A more PowerShell-idiomatic approach would entail using the pipeline with the Where-Object filter cmdlet:
#($array1;$array2) |Where-Object {$_ -eq $searchname}
Use the PS3+ -in operator: $value -in $array
or the PS2+ -contains operator: $array -contains $value
In case of big arrays don't concatenate them as it's slow.
Organize the arrays in an array so you can enumerate them easier.
$arrays = #(
#("name1", "name2", "name3")
#("name4", "name5", "name6")
)
$searchname = Read-Host "Enter the name to search for"
1..$arrays.count | ForEach {
if ($searchname -in $arrays[$_-1]) {
Write-Host "$searchname found in array #$_"
}
}
Or use a hashtable:
$arrays = #{
foo = "name1", "name2", "name3"
bar = "name4", "name5", "name6"
}
$searchname = Read-Host "Enter the name to search for"
ForEach ($entry in $arrays.GetEnumerator()) {
if ($searchname -in $entry.value) {
Write-Host "$searchname found in array $($entry.key)"
}
}