PowerShell icon indicating copy to clipboard operation
PowerShell copied to clipboard

Start-Job -InitializationScript should support $using: variable references too

Open mklement0 opened this issue 8 years ago • 9 comments

Note:

  • The bug equally affects Start-ThreadJob.

In the script block passed to the (implied as the 1st positional) -ScriptBlock parameter of Start-Job, it is possible to reference variables from the calling scope via the $using: scope; e.g.:

# Make the background job's working directory the same as the calling session's and
# print the then-current location.
Start-Job { Set-Location $using:PWD; $PWD } | Receive-Job -Wait -AutoRemove

By contrast, the following - even though I'd expect it to be equivalent - does NOT work, because the $using: reference is in the -InitializationScript script block:

Start-Job -InitializationScript { Set-Location $using:PWD }  { $PWD } | Receive-Job -Wait -AutoRemove

Invocation fails with the following error messages:

A Using variable cannot be retrieved. A Using variable can be used only with 
Invoke-Command, Start-Job, or InlineScript in the script workflow. When it is used with
Invoke-Command, the Using variable is valid only if the script block is invoked on
a remote computer.
....

Note that the above command would be the cleanest workaround for not having a -WorkingDirectory parameter (see #4287) to set the working directory explicitly.

Both script blocks execute in the job's session, so they should both support $using: variable references.

Environment data

PowerShell Core v6.0.0-beta.5 on macOS 10.12.6
PowerShell Core v6.0.0-beta.5 on Ubuntu 16.04.3 LTS
PowerShell Core v6.0.0-beta.5 on Microsoft Windows 10 Pro (64-bit; v10.0.15063)
Windows PowerShell v5.1.15063.483 on Microsoft Windows 10 Pro (64-bit; v10.0.15063)

mklement0 avatar Aug 08 '17 21:08 mklement0

@lzybkr Could you please comment - Is this a bug or an enhancement?

iSazonov avatar Aug 09 '17 08:08 iSazonov

It looks like an oversight, so I'll call it a bug.

lzybkr avatar Aug 09 '17 17:08 lzybkr

Possibly obvious statement: This affects PS 5.1 as well.

Name                           Value
----                           -----
PSVersion                      5.1.16299.64
PSEdition                      Desktop
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}
BuildVersion                   10.0.16299.64
CLRVersion                     4.0.30319.42000
WSManStackVersion              3.0
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1

brantb avatar Dec 13 '17 22:12 brantb

Thanks, @brantb; additionally, in Windows PowerShell it also affects Register-ScheduledJob (which doesn't ship with PowerShell Core, at least as of PowerShell Core 6.2.0-rc.1).

mklement0 avatar Mar 18 '19 19:03 mklement0

@PaulHigin any interest here? It's really hard to initialize the state of a job when you can't bring that state in from the caller's scope.

JustinGrote avatar Feb 09 '22 19:02 JustinGrote

I agree this should be fixed. Not sure when I can get to it, but at least it is back on my radar. Thanks for bringing it to my attention.

PaulHigin avatar Feb 09 '22 20:02 PaulHigin

Thanks. To others as a workaround, you can append your init script to the main block and still be able to use USING

[scriptblock]::Create(
  [string]$initScript + [Environment]::NewLine + [string]$scriptblock
)

JustinGrote avatar Feb 09 '22 21:02 JustinGrote

Nice workaround @JustinGrote

Here is working example of it:

$var = "foo"

$init = {
  function f1(){ "${using:var}bar" }
  function f2(){ "${using:var}baz"; f3 }
  function f3(){ "${using:var}qux" }
}

$job = "f1", "f2" | ForEach-Object {
  Start-Job ( [scriptblock]::Create( [string]$init + [Environment]::NewLine + [string]$_ ))}
$job | Wait-Job | Receive-Job

Or assign variable again:

$init = {
  $var=$using:var
  function f1(){ "${var}bar" }
  function f2(){ "${var}baz"; f3 }
  function f3(){ "${var}qux" }
}

And you can also use it as function:

function scriptblock(){ [scriptblock]::Create( [string]$init + [Environment]::NewLine + [string]$args )}

$job = "f1", "f2" | ForEach-Object { Start-Job ( scriptblock $_ )}

Or whole thing shorter:

$v="foo"
$i={$v=$using:v;function f1{"${v}bar"};function f2{"${v}baz";f3};function f3{"${v}qux"}}
$j="f1","f2"|%{sajb([scriptblock]::Create([string]$i+"`n"+$_))};$j|rcjb -Wait

Output

foobar
foobaz
fooqux

Knud3 avatar Apr 21 '23 18:04 Knud3

Please don't close it.

mon-jai avatar Feb 05 '24 18:02 mon-jai