Home > Net >  Pass [switch] inside Start-Job
Pass [switch] inside Start-Job

Time:07-14

There is a script for users to log in, it calls other scripts in turn, depending on the conditions. In order to call scripts separately manually, the [switch]$Silent parameter has been added. Question - how to pass this parameter inside Start-Job? I tried to add to the list of arguments in different ways - the value always falls into the neighboring parameter, regardless of the order. Main script example

Param(
    [string]$location = 'C:\Users',
    [switch]$Silent
)
    Start-Job -FilePath ".\Fonts_Install.ps1" -ArgumentList ($Silent,$location) | Wait-Job

Fonts_Install.ps1

    Param(
        [switch]$Silent = $false,
        [string]$location = '.'
    )
  $path_fonts = "$env:LOCALAPPDATA\Microsoft\Windows\Fonts"
$Registry = "HKCU:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts"

function WriteLog {
    Param ([string]$LogString)
    $Stamp = (Get-Date).toString("yyyy/MM/dd HH:mm:ss")
    $LogMessage = "$Stamp $LogString"
    Add-content $LogFile -value $LogMessage
}
$Logfile = "$env:LOCALAPPDATA\Temp\fonts_install.log"

WriteLog "Silent $Silent"
WriteLog "location $location"
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName PresentationFramework
Add-Type -AssemblyName System.Drawing
Add-Type -AssemblyName PresentationCore

$SourceFolder = "$location\Fonts_Install"
$WindowsFonts = [System.Drawing.Text.PrivateFontCollection]::new()
$Fonts = Get-ChildItem -Path $SourceFolder -Include *.ttf, *.otf -Recurse -File
ForEach ($Font in $Fonts) {
    $Font_Name = $Font.Name
    $font_fullname = $Font.fullname
    if (Test-Path -PathType Leaf -Path "$path_fonts\$Font_Name") {
        WriteLog "Previously installed $Font_Name"
    }
    else {
        Copy-Item $Font -Destination "$path_fonts" -Force -Confirm:$false -PassThru
        $WindowsFonts.AddFontFile("$font_fullname")
        $ValueFont = "$path_fonts"   "\"   "$Font_Name"
        $Typeface = New-Object -TypeName Windows.Media.GlyphTypeface -ArgumentList "$font_fullname"
        [string]$FamilyFaceNames = $Typeface.FamilyNames.Values   $Typeface.FaceNames.Values
        $RegistryValue = @{
            Path  = $Registry
            Name  = $FamilyFaceNames
            Value = $ValueFont
        }
        if (Test-Path $Registry\$FamilyFaceNames) {
            Remove-ItemProperty -name $FamilyFaceNames -path $Registry
        }
        New-ItemProperty @RegistryValue
        WriteLog "New fonts installed $Font_Name"
    }
}  
    switch ($Silent) {
        $false {
            if ($Error.Count -gt 0) {
                for ($i = 0; $i -le ($Error.Items.Count   1); $i  ) {
                    $errMSG = "$Error"
                }
                [System.Windows.Forms.MessageBox]::Show("$errMSG", "Error", "OK", "Error")
            }
            else {
                [System.Windows.Forms.MessageBox]::Show("ок", "Fonts", "OK", "Asterisk") | out-null
            }
        }
    }

CodePudding user response:

Unfortunately, specifying pass-through arguments via Start-Job's -ArgumentList (-Args) is limited to positional arguments, which prevents binding [switch] parameters, whose arguments must by definition be named.

As a workaround, instead of using -FilePath, invoke your script via the -ScriptBlock parameter. Inside of a script block ({ ... }, named arguments may be used in script calls, as usual:

Start-Job -ScriptBlock {
  .\Fonts_Install.ps1 -Silent:$using:Silent $using:Location
} | Receive-Job -Wait -AutoRemoveJob
  • Note the use of the $using: scope in order to embed variable values from the caller's scope in the script block that will execute in the background.

  • You still need to refer to the -Silent parameter by name, and the whether the switch is on or off can be communicated by appending :$true or :$false to it, which is what :$using:Silent does.

CodePudding user response:

Here is a different alternative to mklement0's helpful answer, this answer does not use Start-Job and uses a PowerShell instance instead, using this method we can leverage the automatic variable $PSBoundParameters.

Do note, that for this to work properly, both .ps1 scripts must share the same parameter names or Alias Attribute Declarations that matches the same parameter from the caller. See this answer for more details.

You can use these snippets below as a example for you to test how it works.

  • caller.ps1
param(
    [string] $Path = 'C:\Users',
    [switch] $Silent
)

try {
    if(-not $PSBoundParameters.ContainsKey('Path')) {
        $PSBoundParameters['Path'] = $Path
    }

    $ps = [powershell]::Create().
        AddCommand('path\to\myScript.ps1').
        AddParameters($PSBoundParameters)

    $iasync = $ps.BeginInvoke()
    # Do something else here while the .ps1 runs
    # ...
    # Get async result from the PS Instance
    $ps.EndInvoke($iasync)
}
finally {
    if($ps -is [IDisposable]) {
        $ps.Dispose()
    }
}
  • myScript.ps1
# Note, since we're bounding this parameters from the caller.ps1,
# We don't want to assign Default Values here!
param(
    [string] $Path,
    [switch] $Silent
)

foreach($param in $MyInvocation.MyCommand.Parameters.Keys) {
    [pscustomobject]@{
        Parameter = $param
        Value     = Get-Variable $param -ValueOnly
    }
}

A few examples:

PS /> .\caller.ps1

Parameter Value
--------- -----
Path      C:\Users
Silent    False

PS /> .\caller.ps1 -Path hello

Parameter Value
--------- -----
Path      hello
Silent    False

PS /> .\caller.ps1 -Path world -Silent 

Parameter Value
--------- -----
Path      world
Silent    True
  • Related