29

I'm trying pass an environment variable to Node.js with PowerShell like this way:

C:\Users\everton\my-project> $env:MY_VAR = 8000 node index.js

But I get an error in PowerShell:

Token 'node' unexpected expression or statement

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Everton Santos
  • 472
  • 1
  • 4
  • 8

3 Answers3

44

Set environmental variable MY_VAR first and run your app like this:

C:\Users\everton\my-project> $env:MY_VAR="8000" ; node index.js

You can access environmental variable MY_VAR inside index.js by

process.env.MY_VAR

Note: PowerShell doesn't directly support command-scoped environment variables. The above command sets the environment variable for that PowerShell session.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Harikrishnan
  • 9,688
  • 11
  • 84
  • 127
  • 7
    This sets the command for the whole "bash" session, this is not command scoped – Cyril CHAPON Sep 21 '17 at 11:09
  • 2
    Indeed and that's worth mentionning. Also worth mentionning my previous comment. Imagine somebody coming here to look for a solution on a production environment. He runs your command to troubleshoot an incident and run his command with `NODE_ENV=dev` to get node.js tracing. He fixes the trouble then he restarts his server with `node index.js`. Except he doesn't know he's still in `NODE_ENV=dev` and added 500ms latency on every page view on his website. The reasonnable answer is "PowerShell can't do that, use the right tool or workaround like this" – Cyril CHAPON Oct 29 '18 at 09:04
8

Note: If you can assume that Node.js is already installed - as is by definition the case when you're invoking node - consider use of npm helper packages, as shown in Cyril CHAPON 's helpful answer.
This answer focuses on generic solutions from within PowerShell.

tl;dr

# Set env. variable temporarily, invoke the external utility, 
# then remove / restore old value.
$oldVal, $env:MYVAR = $env:MYVAR, 8000; node index.js; $env:MYVAR = $oldVal
# Scoped alternative that uses a *transient* helper variable.
& { $oldVal, $env:MY_VAR = $env:MY_VAR, 8000; node index.js; $env:MY_VAR = $oldVal }

More simply, if there's no preexisting MY_VAR value that must be restored.

$env:MYVAR=8000; node index.js; $env:MYVAR=$null

See below for an explanation and an alternative based on a helper function.


To complement Harikrishnan's effective answer:

PowerShell has no equivalent to the command-scoped method of passing environment variables that POSIX-like shells offer (as of PowerShell v7 - however, introducing it to PowerShell - not necessarily with the same syntax - is being discussed in GitHub issue #3316); e.g.:

 # E.g., in *Bash*: 
 # Define environment variable MY_VAR for the child process being invoked (`node`)
 # ONLY; in other words: MY_VAR is scoped to the command being invoked
 # (subsequent commands do not see it).
 MY_VAR=8000 node index.js

In PowerShell, as Harikrishnan's answer demonstrates, you have to define the environment variable first, and then, in a separate statement, call the external program, so $env:MY_VAR="8000"; node index.js is the right PowerShell solution, but it is worth nothing that $env:MY_VAR stays in scope for the remainder of the session (it is set at the process level).

Note that even using a script block invoked with & to create a child scope doesn't help here, because such child scopes only apply to PowerShell variables, not environment variables.

You can, of course, remove the environment variable manually after the node call:
Remove-Item env:MY_VAR or even just $env:MY_VAR = $null, which is what the 1st command at the top shows.


A more structured alternative - perhaps better in the case of setting multiple environment variables and/or invoking multiple commands - is to use a script block invoked with &:

& { $oldVal, $env:MY_VAR = $env:MY_VAR, 8000; node index.js; $env:MY_VAR = $oldVal }

This takes advantage of:

  • { ... } is a script block that provides a clearly visible grouping for the commands in it; invoked with &, it creates a local scope, so that helper variable $oldVal automatically goes out of scope on exiting the block.

  • $oldVal, $env:MY_VAR = $env:MY_VAR, 8000 saves the old value (if any) of $env:MY_VAR in $oldVal while changing the value to 8000; this technique of assigning to multiple variables at once (known as destructuring assignment in some languages) is explained in Get-Help about_Assignment_Operators, section "ASSIGNING MULTIPLE VARIALBES".

Alternatively, use the helper function below, or use a try { ... } finally { ... } approach, as demonstrated in this related answer.


Helper function for command-scoped environment modifications.

If you define the helper function below (remember that function definitions must be placed before they're invoked), you can achieve command-scoped modification of your environment as follows:

# Invoke `node index.js` with a *temporarily* set MY_VAR environment variable.
Invoke-WithEnvironment @{ MY_VAR = 8000 } { node index.js }

Invoke-WithEnvironment() source code:

function Invoke-WithEnvironment {
<#
.SYNOPSIS
Invokes commands with a temporarily modified environment.

.DESCRIPTION
Modifies environment variables temporarily based on a hashtable of values,
invokes the specified script block, then restores the previous environment.

.PARAMETER Environment
A hashtable that defines the temporary environment-variable values.
Assign $null to (temporarily) remove an environment variable that is
currently set.

.PARAMETER ScriptBlock
The command(s) to execute with the temporarily modified environment.

.EXAMPLE
> Invoke-WithEnvironment @{ PORT=8080 } { node index.js }

Runs node with environment variable PORT temporarily set to 8080, with its
previous value, if any 
#>
  param(
    [Parameter(Mandatory)] [System.Collections.IDictionary] $Environment,
    [Parameter(Mandatory)] [scriptblock] $ScriptBlock
  )
  # Modify the environment based on the hashtable and save the original 
  # one for later restoration.
  $htOrgEnv = @{}
  foreach ($kv in $Environment.GetEnumerator()) {
    $htOrgEnv[$kv.Key] = (Get-Item -EA SilentlyContinue "env:$($kv.Key)").Value
    Set-Item "env:$($kv.Key)" $kv.Value
  }
  # Invoke the script block
  try {
    & $ScriptBlock
  } finally {
    # Restore the original environment.
    foreach ($kv in $Environment.GetEnumerator()) {
      # Note: setting an environment var. to $null or '' *removes* it.
      Set-Item "env:$($kv.Key)" $htOrgEnv[$kv.Key]
    }
  }
}
mklement0
  • 382,024
  • 64
  • 607
  • 775
8

My answer require the use of Node.js and npm libraries.

...or you just take out the pain of writing obscure-WTF-language-scripting, and use one of command-scoped (plus cross-platform) Node.js scripts:

  • cross-env (for inline)

    cross-env MYVAR=MYVALUE node index.js
    
  • env-cmd (from .env file)

    env-cmd .env node index.js
    

    with

    #.env file
    MYVAR=MYVALUE
    
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Cyril CHAPON
  • 3,556
  • 4
  • 22
  • 40