0

I'm trying to figure out how to simplify this process, but it's not as simple as I thought.

I have a config file that looks similar to this:

[string][1][options]
$List = @(
   "c:\path\to\file,1,-a,-b,-c,-d,-e"
)

The only items required are the [string] and the [1]. There are 10 options (-a, -b etc), potentially more. Each of which is optional and could be supplied in any order.

In the main script I then do the following at present:

foreach ($a in $List) {
    $dataSplit = $a -split"(,)"
    $string = $dataSplit[0]
    $number = $dataSplit[2]
    $ds4 = $dataSplit[4]
    if(!$ds4) { 
        $ds4 = "0" 
    } elseif($ds4.StartsWith("-a")) {
        $a_set = 1
        write-host "a_set has been set to $a_set"
    } elseif($ds4.StartsWith("-b")) {
        $b_set = 1
        write-host "b_set has been set to $b_set"
    }
    . . .
    if(!$ds5) { 
        $ds5 = "0" 
    }
    . . .

As you can imagine this gets quite long. So I thought I would simplify it with a function. e.g.

function get-additional($item) {
    if($item.StartsWith("-a")) {
        $a_set = 1
        Write-Host "$a_set has been set"
        return $a_set
    }
    if($item.StartsWith("-b")) {
        $b_set = 1
        Write-Host "$b_set has been set"
        return $b_set
    }
}

And then call it thus:

if(!$ds4) { 
    $ds4 = "0"
} else {
    get-additional($ds4)
}

Is there a way to do this? I've seen pleanty of examples if you only have a single variable to return, or even a fixed number, but none that allow for the return of 'one of many' variables.

Here is the (shortened) script in one if it helps:

$List = @(
"c:\path\to\file,1,-b,-c,-d,-e"
)

function get-additional($item) {
    if($item.StartsWith("-a")) {
        $a_set = 1
        Write-Host "a_set has been set to $a_set"
        return $a_set
    }
    if($item.StartsWith("-b")) {
        $b_set = 1
        Write-Host "b_set has been set to $b_set"
        return $b_set
    }
}

$a_set = 0
$b_set = 0
$c_set = 0

foreach ($a in $List) {
    $dataSplit = $a -split"(,)"
    $string = $dataSplit[0]
    $number = $dataSplit[2]
    $ds4 = $dataSplit[4]
    Write-Host "ds4 = $ds4"
    if(!$ds4) {
        $ds4 = "0"
    } else {
        get-additional($ds4)
    }
    $ds5 = $dataSplit[6]
    Write-Host "ds5 = $ds5"
    if(!$ds5) {
        $ds5 = "0"
    } else {
        get-additional($ds5)
    }
}

Write-Host "a = $a_set"
Write-Host "b = $b_set"       

The desired result at the end would be

a = 0
b = 1

- - - UPDATE 2015-11-30 16:54

In case it helps to understand what I am going for here's a Sample from my actual script

$cfg_AppList = @(
"C:\Path\to\application1\app1.exe instance1,1"
"C:\Path\to\application2\app2.exe instance2,1,-p12345"
"C:\Path\to\application3\app3.exe instance3,0"
"C:\Path\to\application3\app3.exe instance3,1,-p78901"
)

function get-additional($item)
{
    $script:pval = "0"

    if($item.StartsWith("-p"))
        {
            $script:pval = $ds4.substring(2)
            write-host "$pval is a pval" 
        }
}

$AppObject = @()
foreach($a in $cfg_AppList)
    {
        $dataSplit = $a -split","
        $AppVal = $dataSplit[0]
        $checkVal = $dataSplit[1]
        $ds4 = $dataSplit[2]

        if(!$ds4) 
            { 
                $ds4 = "0" 
            }
        else
            {
                get-additional($ds4)
            }

        $AppObject += New-Object PSObject -property @{
            AppVal = "$AppVal";
            checkVal = "$checkVal";
            pval = "$pval";
            }
    }   

The $AppObject object is then referenced and updated as the script progresses. The values supplied in pval and (see below eval) will determine what happens.

I now need to add a second element -e which will be included thus:

 $cfg_AppList = @(
"C:\Path\to\application1\app1.exe instance1,1"
"C:\Path\to\application2\app2.exe instance2,1,-p12345"
"C:\Path\to\application3\app3.exe instance3,0,-e"
"C:\Path\to\application3\app3.exe instance3,1,-e,-p78901"
)

It will be either selected 1 or not selected 0, and added to the $AppObject Array as eval=$eval (1|0).

Going forward I have more options I plan to introduce, hence the need to find the most efficient way to handle them all.

- - - UPDATE 2015-12-01 11:39

OK, What I have gone with is a combination of both ideas below. Placing the options into an array and looping through them, then using a SWITCH statement to see which ones are set.

$AppObject = @()
foreach($a in $cfg_AppList)
    {
        $pval = 0
        $eval = 0

        $AppVal,$CheckVal,$options = $a -split","

        foreach($opt in $options)
            {
                switch -wildcard ($opt) 
                    {
                       '-p*' { $pval = $opt.substring(2) }
                       '-e'  { $eval = 1 }
                    }
            }      

        $AppObject += New-Object PSObject -property @{
            AppVal = "$AppVal";
            CheckVal = "$CheckVal";
            pval = "$pval";
            eval = "$eval";
            }            
    }
1
  • Sorry, but after your explanation I'm more confused than before. What options do you plan to introduce? What is their purpose? Or the purpose of the $AppObject array? Why do you think you need to name a property of your custom objects after the options from your string instead of using a generic name? Commented Nov 30, 2015 at 21:12

2 Answers 2

1

First off, don't capture the , in your split operation if you're not planning to use it for anything, just use -split "," (no parentheses).

We can make use of multiple variable assignment to "shift" away to string and number 1:

$s,$n,$opts = "string,1,-a,-b,-c" -split ","

$opts will now contain the string array: @("-a","-b","-c")

The easiest way to check for whether a predetermined set of options is present or not, is to simply loop through all possible options and see if they are contained in the input string:

function Parse-InputString 
{
    param($InputString)

    # prepare the options you want to check for
    $PossibleOptions = "abcde".ToCharArray()

    # Split the input string 
    $String,$Number,$Options = $InputString -split ","

    # Create a new object with the string and number values
    $OutputObject = New-Object psobject -Property @{
        "String" = $String
        "Number" = $Number
    }

    # Now inspect the $Options array to see if any of them are set
    foreach($PossibleOption in $PossibleOptions){

        $OptionSet = if($Options -contains "-$PossibleOption"){
            1
        } else {
            0
        }

        # Add the information to the object
        $OutputObject |Add-Member -MemberType NoteProperty -Name $PossibleOption -Value $OptionSet
    }

    # return the object carrying all the information
    return $OutputObject
}

Now you can have your input string parsed nicely into an actual object:

PS C:\> Parse-InputString -InputString "c:\path\to\file,1,-b,-c,-d,-e"

Number : 1
String : c:\path\to\file
a      : 0
b      : 1
c      : 1
d      : 1
e      : 1
Sign up to request clarification or add additional context in comments.

1 Comment

thanks, I've been toying with this as it looks like it may be what I need, but can't quite work it out, I think maybe I didn't explain very well initially. I've added some extra to further explain what I am going for.
0

The easiest way would be to update the global variables in your function without returning anything:

function Get-Additional($item) {
    if ($item.StartsWith("-a")) {
        $global:a_set = 1
        Write-Host "a_set has been set to $a_set"
    }
    if ($item.StartsWith("-b")) {
        $global:b_set = 1
        Write-Host "b_set has been set to $b_set"
    }
}

However, modifying global variables in functions is not a good practice, because it's difficult to debug. I wouldn't recommend going this route.

A better approach is to pass your current values as parameters into the function, return the modified values, and assign them back to variables.

function Get-Additional($item, $a, $b) {
    if ($item.StartsWith("-a")) {
        $a = 1
        Write-Host "a_set has been set to $a_set"
    }
    if ($item.StartsWith("-b")) {
        $b = 1
        Write-Host "b_set has been set to $b_set"
    }

    @($a, $b)
}

$set_a, $set_b = Get-Additional $ds4 $set_a $set_b

In the above sample the function returns a list of the modified values (@($a, $b)), which are then assigned back to the list $set_a, $set_b. The return keyword is not required for returning something from a PowerShell function. It controls only where to return from a function, not what to return.

With that said, for your scenario I wouldn't use a function in the first place. A switch statement would be better suited for this kind of manipulation:

switch -wildcard ($ds4) {
  '-a*' { $set_a = 1 }
  '-b*' { $set_b = 1 }
}

4 Comments

Cheers, I tried expanding the second option above to include 10 possible items (a - j), and 10 checks ($ds4 - $ds14). I then supplied 4 options (-c,-b,-d,-i) to the string. But I get set_c has been set to 0 in the function, followed by set_b has been set to with no value (and for set_d and set_i). Then at the end where I check the result write-host "a = $set_a" results in System.Object[] 1. All the others have no result, apart for i which is 1.
I tried it with the switch statement too, and whilst it works, it takes nearly 100 lines vs the function at less than 50. It also includes a lot of repetition of data, which I was always told is a bad thing(!)
Please update your question with the modified code and the results it produces. Also, please explain what you're trying to achieve in the end, i.e. where do these values come from and what do you need them for?
I've added some extra (above) to try and explain what I am going for.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.