3

I want to call the specific function in my powershell script from command line. But It return me error like this : Key cannot be null.

Parameter name: key
$Get = $ini_file.($a_section).($b_node)

Key cannot be null.
Parameter name: key
$Output = $ini_file.($a_section).($b_node) | Out-File $Store\Val ...

Here is my code:

Param(
    [Parameter(Mandatory=$true)]
    [string]$FilePath,
    $ini, $a_section, $b_node, $c_store,
    $a, $b, $c
)

function Get-File {
    $input_file = $FilePath
    $ini_file = @{}

    Get-Content $input_file | ForEach-Object {
        $_.Trim()
    } | Where-Object {
        $_ -notmatch '^(;|$)'
    } | ForEach-Object {
        if ($_ -match '^\[.*\]$') {
            $section = $_ -replace '\[|\]'
            $ini_file[$section] = @{}
        } else {
            $key, $value = $_ -split '\s*=\s*', 2
            $ini_file[$section][$key] = $value
        }
    }

    $Get = $ini_file.($a_section).($b_node)
    $Store = $c_store
    $Output = $ini_file.($a_section).($b_node) | Out-File $Store\Value.cmd
}

function Write-File {
    Import-Module PsIni
    $ff = Get-IniContent $FilePath
    $ff["$a"]["$b"] = "$c"  
    $ff | Out-IniFile -FilePath $FilePath -Force
}

# Calling the function with the global parameter $FilePath
Write-File $FilePath $a $b $c 
Get-File $FilePath $ini $a_section $b_node $c_store

The way I call each function from command line is:

Call function Get-File:

powershell.ps1 Get-File -FilePath C:\User\file.ini -a_section Information -b_node Name -c_store C:\User\

If I run that command, I still can get the value of INI file, but it returns the above error. If I REMOVE this part Write-File $FilePath $a $b $c It will not return any error.

It looks like I can't use "calling the function" at the same time. But I am not sure about it.

My INI file

    [Information]
    Name=Joyce
    Age=12
    

UPDATED CODE:

Function Read-File
{
    Param(
    [parameter(mandatory=$true)]$FilePath,
    [parameter(mandatory=$true)] $ini,
    [parameter(mandatory=$true)] $a_section,
    [parameter(mandatory=$true)] $b_node,
    [parameter(mandatory=$true)] $c_store
    )
    $input_file = $FilePath
    $ini_file = @{}

    Get-Content $input_file | ForEach-Object {
    $_.Trim()
    } | Where-Object {
    $_ -notmatch '^(;|$)'
    } | ForEach-Object {
    if ($_ -match '^\[.*\]$') {
        $section = $_ -replace '\[|\]'
        $ini_file[$section] = @{}
    } else {
        $key, $value = $_ -split '\s*=\s*', 2
        $ini_file[$section][$key] = $value
    }
    }

     $Get = $ini_file.($a_section).($b_node)
     $Store = $c_store
     $Output = $ini_file.($a_section).($b_node) | Out-File $Store\Value.cmd
}

#---------------------------------------------------------------#
Function Write-File
{
    Param(
    [parameter(mandatory=$true)]$FilePath,
    [parameter(mandatory=$true)] $a,
    [parameter(mandatory=$true)] $b,
    [parameter(mandatory=$true)] $c
    )
    Import-Module PsIni
    $ff = Get-IniContent $FilePath
    $ff["$a"]["$b"] = "$c"  
    $ff | Out-IniFile -FilePath $FilePath -Force

}

$cmd, $params = $args
& $cmd @params
1
  • This code is working Commented Mar 27, 2019 at 3:05

1 Answer 1

9

The way you're trying to invoke the function does not work, because you're passing arguments to the script, but never do anything with them. If you want the function to be invoked automatically when the script is run invoke the function from within the script.

function Write-IniFile {
    Param(
        ...
    )
    ...
}
Write-IniFile -FilePath C:\Users\file.ini -a Name -b Joyce -c 1309

and run the script like this:

PS.ps1

Otherwise dot-source the script before invoking the function:

. PS.ps1
Write-IniFile -FilePath C:\Users\file.ini -a Name -b Joyce -c 1309

If you want to be able to run the script with parameters and have those passed to the function use the first approach, but splat $args on the function:

function Write-IniFile {
    Param(
        ...
    )
    ...
}
Write-IniFile @args

and invoke the script like this:

PS.ps1 -FilePath C:\Users\file.ini -a Name -b Joyce -c 1309

Edit:

If you have a script with multiple functions and want to be able to invoke any of them by running the script you could split'n'splat the script parameters:

PS.ps1:

function Foo {
    Param(
        [Parameter(Mandatory=$true)]
        $x
    )
    ...
}

function Bar {
    Param(
        [Parameter(Mandatory=$true)]
        $y,
        [Parameter(Mandatory=$true)]
        $z
    )
    ...
}

$cmd, $params = $args
& $cmd @params

Invocation:

PS.ps1 Foo -x 'some'          # invoke Foo() with the given arguments
PS.ps1 Bar -y 'or' -z 'other' # invoke Bar() with the given arguments

The statement

$cmd, $params = $args

assigns the first argument to the variable $cmd and the rest of the arguments to the variable $params. The next statement

& $cmd @params

invokes $cmd via the call operator (&) and splats the parameters in $params.

Sign up to request clarification or add additional context in comments.

6 Comments

But my expectation, when I have 2 or 3 function, I can use this command PS.ps1 function1 FilePath -a Name -b Joyce -c 1309 if Ijust want to execute function1. PS.ps1 function2...... If I just wwan to execute function2
@Job You should have explained that in your question. See updated answer.
It still not solve my problem. I updated the question.
@Job Because you removed the parameter definitions from your functions and put a global parameter definition in your script. Where did my answer suggest to do that? Anyway, updated for clarity.
I create new updated code. Is that what u mean? @Ansgar Wiechers?
|

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.