Vscode: Environment variables defined in `tasks.json::tasks.options.env` do not overwrite existing environment variables

Created on 16 Apr 2018  路  47Comments  路  Source: microsoft/vscode

Code: 1.22.2

macOS: 10.13.3

When I create a task, I want to provide a very specific environment (which is why I bother filling out the JSON values). However, if an environment variable is pre-existing, it will not be overwritten in the execution environment of the task.

I am extremely surprised by this behavior. I discovered this by not being able to find a library I was building with a task when specifying DYLD_LIBRARY_PATH.

A very simple test is to update ~/.bash_profile to include

export FOO=bar

Then attempt to override it with tasks.json

{
    // See https://go.microsoft.com/fwlink/?LinkId=733558
    // for the documentation about the tasks.json format
    "version": "2.0.0",
    "tasks": [
        {
            "label": "diagnostic",
            "type": "shell",
            "options": {
                "cwd": "${workspaceFolder}/beam",
                "env": {
                    "FOO": "baz",
                }
            },
            "command": "printenv",
            "problemMatcher": []
        }
    ]
}

Output:

...
FOO=bar
...

A "task" appears to execute . ~/.bash_profile to set it's environment. Since it is already creating a new environment for each execution, it would be nice if I could override the values as I see fit, by specifying the values in tasks.options.env.

bug tasks

Most helpful comment

This issue and the handling of "env" options in tasks.json is kind of confusing.
I am developing on Windows and Linux (and prototyping for the latter on WSL) and adding to what @zfields reported, I have created this tasks.json.

{
    "version": "2.0.0",
    "tasks": [
        {
            "label": "check (bash)",
            "type": "shell",
            "windows": {
                "options": {
                    "shell": {
                        "executable": "bash.exe",
                        "args": [ "-c" ]
                    }
                }
            },
            "linux" : {
                "options": {
                    "shell": {
                        "executable": "bash",
                        "args": [ "-c" ]
                    }
                }
            },
            "options": {
                "env": {
                    "BAR" : "BAZ"
                },
            },
            "command": "echo env{FOO}=${env:FOO} env{BAR}=${env:BAR} var{BAR}=$BAR",
            "problemMatcher": []
        },
        {
            "label": "check (cmd)",
            "type": "shell",
            "windows": {
                "options": {
                    "shell": {
                        "executable": "cmd.exe",
                        "args" : [ "/C" ]
                    }
                }
            },
            "options": {
                "env": {
                    "BAR" : "BAZ"
                }
            },
            "command": "echo env{FOO}=${env:FOO} env{BAR}=${env:BAR} var{BAR}=%BAR%",
            "problemMatcher": []
        }
    ]
}

Results (without FOO or BAR set/exported in the parent shell that executes code)

  1. Windows cmd.exe env{FOO}= env{BAR}= var{BAR}=BAZ
  2. WSL bash.exe env{FOO}= env{BAR}= var{BAR}=
  3. Linux/Mac OS bash env{FOO}= env{BAR}= var{BAR}=BAZ

Results (with set/exported FOO=foo BAR=bar in the parent shell that executes code)

  1. Windows cmd.exe env{FOO}=foo env{BAR}=bar var{BAR}=BAZ
  2. WSL bash.exe env{FOO}=foo env{BAR}=bar var{BAR}=
  3. Linux/Mac OS bash env{FOO}=foo env{BAR}=bar var{BAR}=BAZ

I am assuming that Mac OS behaves like Linux.
_Update 2019/02/13: MacOS behaves like Linux._

Tested with Visual Studio Code 1.31.

Bottom line is:

  • ${env:...} only expands environment variables that were set in the parent shell that ran code. It doesn't expand variables set in the tasks.json env options.
  • %var% expands env variables from the tasks.json options when running cmd.exe. So does $var for real Linux bash. But on WSL, $var doesn't expand env variables from the tasks.json options.
  • %var% (cmd.exe) and $var (Linux bash) may expand to values different from ${env:var} if var was set in code's parent shell.

Is this a bug or by design??

FWIW, I would prefer if ${env:var} would also expand the env options of the tasks.json file, superseding everything that was set in the environment of code's parent shell. If ${env:var} is the standard way to expand environment (and env options) variables, the task's command may be unified across platforms because there's no need for $var vs. %var%.

If not a bug, would that be a feature request?

All 47 comments

Since this is a shell task the bash decides to source ~/.bash_profile. This is how shells work and there is not much to my knowledge that VS Code can do to avoid this.

The shell started has the -c option so the shell source /.bashrc which normally has

# If not running interactively, don't do anything
case $- in
    *i*) ;;
      *) return;;
esac

at its being. So every instructions you have after that are not executed if the -i option is not present. So I recommend that you move the environment variables into ./bashrc.

OK to close

NOT ok to close.

How is VSCode setting the environment variables I request in tasks.options.env?

It is setting the environment for the launched shell process. Tasks don't start login shells so the ./bash_profile shouldn't be executed.

Do you have a Mac to reproduce this with? Use diagnostic task I pasted above. It will reflect any variables specified in ~/.bash_profile.

To reproduce:

  1. Use a Mac?
  2. Set an environment variable in ~/.bash_profile
  3. Modify the environment variable by using the shell in VSCode (optional)
  4. Add it with an alternate value to the diagnostic task.
  5. Execute the diagnostic task (_you will observe the value of the environment variable you set in ~/.bash_profile._)
  6. Change the value in ~/.bash_profile, and watch it propagate to value printed in the diagnostic task.

The value from ~/.bash_profile will display when you run the diagnostic task (regardless of any value set on the task itself).

@zfields I understand the problem and it is independent of the OS at the end. Since VS Code doesn't want to temper with what shells do when executed you can't override any variables that are later on sourced from somewhere else.

I tested this and ~/.bash_profile is not sourced when running a task for me. What happens when you simply start bash without --login. Will it source ~/.bash_profile

I think we are talking past each other, maybe we should take two steps back...

I have no desire to use a shell. In fact, I ONLY want to use VSCode.

However, when running a "shell task" in VSCode (_i.e. diagnostic from above_), I happened to notice VSCode picks up environment variables _that are only set_ in my ~/.bash_profile. If you are not able to reproduce this on your non-Apple device, then perhaps it IS platform specific.

I think we need to make sure we are experiencing the same behavior, before we can attempt to solve this problem.

I tried this on Apple and Linux and in both cases the ~/.bash_profile was not source for me unless I used a login shell. This is why I ask you what happens for you when you run bash from a different shell or even form a terminal already running bash without --login. Will the code from ~/.bash_profile be executed.

I'm sorry, I am completely confused.

I am not trying (and do not wish) to use any shell whatsoever, I am only trying to use the "tasks" detailed by tasks.json from the VSCode UI, on a Mac.

Your directives about running bash with different parameters and using different shells make no sense to me - unless those are options that need to be entered into the tasks.json file itself.

Can you deliberately spell out what you mean by "this" in the following sentence?

I tried this on Apple and Linux...

Meanwhile, I will try to shutdown my machine completely. Only open VSCode and no other applications, and see if the environment variables listed in ~/.bash_profile appear when I run the diagonostic "task" I have provided to you above.

Futhermore, I WILL NOT be opening a shell (deliberately), unless it is done so on my behalf, without my knowing, by VSCode or macOS.

Just to confirm...

  1. I cold booted my Mac
  2. Opened VSCode to a new folder on the system
  3. Added a new task "diagnostic", based on the "Others" template

    {
       // See https://go.microsoft.com/fwlink/?LinkId=733558
       // for the documentation about the tasks.json format
       "version": "2.0.0",
       "tasks": [
           {
               "label": "diagnostic",
               "type": "shell",
               "command": "printenv"
           }
       ]
    }
    
  4. I execute the task from the "_Tasks >> Run Task..._" menu

    screen shot 2018-04-22 at 10 52 37 pm

  5. In the output appearing in the "TERMINAL" tab of VSCode UI, I can see environment variables only declared in ~/.bash_profile

    screen shot 2018-04-22 at 10 50 09 pm

How is this happening, if executing a task from the UI in VSCode is not loading environment varialbes from ~/.bash_profile?

If you create a tasks of type shell then VS Code starts a shell to execute the command. In our case printenv. The shell used is the one configured as the integrated shell for VS Code using the setting "terminal.integrated.shell.osx". Did you configure any shell args for the integrated shell ?

No, I wasn't aware the setting existed, so any behavior is the default behavior of VSCode. Following on, does it stand to reason that VSCode does in fact consume ~/.bash_profile?

If so, are you able to inject the environment variables I have supplied in tasks.options.env, _AFTER_ it consumes ~/.bash_profile? If you can, it would truly be helpful when constructing an environment in which to execute a task (_the crux of the issue at hand_).

I haven't heard anything in a few days, and the issue is still marked as "needs more info". I just wanted to make sure you are not waiting on me.

Sorry, we are in end game. If you want to inject something into the shell the best is to build a command that does both. For example 'set X="abc" & myCommand'.

What happens when you open an integrated terminal and execute printenv in it. Does it source things from the profile as well.

What happens when you open an integrated terminal and execute printenv in it. Does it source things from the profile as well.

Yes.

Sorry, we are in end game. If you want to inject something into the shell the best is to build a command that does both. For example 'set X="abc" & myCommand'.

What is the point of specifying tasks.options.env, if putting everything in the command string is the only reliable way to ensure the environment variables are getting set?

It seems like I have identified a bug for you, and you have identified a solution. However, you are suggesting that I hack it in place, instead of VSCode correctly assembling the command string by prepending the setting of environment variables to the specified command.

To use your example, VSCode is taking a command string from me, and also has all the environment variables I wish to have set.

{
...
  "options": {
    "env": {
      "X": "abc"
    }
  },
  "command": "myCommand",
...
}

Why isn't VSCode capable of constructing the following string on my behalf, before passing myCommand to its internal command line API?

set X="abc" && myCommand

_Note the "double ampersand" to ensure they run synchronously and complete successfully_

What is the best way to set new env var and or overwrite existing PATH var required only for test build on machine without admin rights?

.NET classic is used

Yes.

This is really strange since values from the profile should only be sourced when --login is specified and the rc files usually have interactive check at the beginning. Have you configured anything special on our machine.

We don't inject something into the shell to keep it consistent with the process type were we can't do such tricks and rely on what the process sees when executed.

I will keep the item open to consider it as a feature request to treat this different for shells

@hellboy81 usually you should be able to overwrite the PATH by specifying the "env" in the "options". Is this not working for you?

Have you configured anything special on our machine.

Did you mean "your" machine? If so, then no, I have not.

... a feature request to treat this different for shells

Do you mean "shell tasks"? Shell tasks are the only thing I am addressing in this issue.

Based on my experience thus far, I would not expect @hellboy81 to be able to overwrite an existing environment variable (i.e. PATH), using "env" in the "options"; the namesake of this issue.

I meant your machine. I read a little bit more about it and it looks like under MacOS it depends on which terminal is used whether the ./bash_profile is source or nor for non login shells. The Terminal.app doesn't follow the convention of only sourcing ./bash_profile for login shells. I tested it with the Terminal.app and there I see it source every time :-(

I've lost your train of thought. Can you take a moment to explain what the results of your Terminal.app test mean in the context of "shell tasks" in VSCode and how it affects this issue?

Sorry for the short answer. Normally bash defines that ~/.bash_profile should only be considered when the shell / terminal is a login shell (for example when you login over ssh into a machine). However the standard Mac Terminal application doesn't stick to this rule and considers the content of ~/.bash_profile for all started shell (hence the behavior you are seeing). When I retested this today I used the standard Mac Terminal as was able to reproduce the behavior you are seeing.

Now that we are on the same page with the behavior, shouldn't this be marked a bug - instead of a feature request?

I specify environment variables in my task, but I do not see them in the environment in which VSCode executes the task.

In other words, the behavior does not meet reasonable expectations, and is impacting my workflow (_smells like a bug_). The main reason I am pushing for the distinction is because I know "bugs" get prioritized and fixed while "feature requests" get punted.

_To provide some context, I work at a cross platform shop, and I'm trying to get them to switch over to VSCode instead of using native IDEs for each platform. As you know, people resist change and things like this make it easy for people to pick apart, even when it is an awesome product. Please help me get this fixed._

I see your point. On the other hand we need to work around a bug caused by another tool :-).

I am still not sure if we should try to fix this on our end since it will for sure introduce another set of problems (command line length if a lot of environment variables are set, ...). When reading about it people advised the following trick:

  • have a ~/.bashrc with this at the beginning
# If not running interactively, don't do anything
case $- in
    *i*) ;;
      *) return;;
esac
  • in your profile have
if [ -f ~/.bashrc ]; then
   source ~/.bashrc
fi
  • set the variable in the ~/.bashrc file
export FOO=bar

I haven't tested this but I think it should work around the problem for now.

I'll try it out

~/.bash_profile

if [ -f ~/.bashrc ]; then
   source ~/.bashrc
fi

export BAZ=bat

~/.bashrc

# If not running interactively, don't do anything
case $- in
    *i*) ;;
      *) return;;
esac

export PATH=/Users/zfields/.bin/:$PATH
export DYLD_LIBRARY_PATH=/Users/zfields/.lib:$DYLD_LIBRARY_PATH
export FOO=bar

tasks.json

{
    // See https://go.microsoft.com/fwlink/?LinkId=733558
    // for the documentation about the tasks.json format
    "version": "2.0.0",
    "tasks": [
        {
            "label": "__diagnostic",
            "type": "shell",
            "options": {
                "cwd": "${workspaceFolder}/bar",
                "env": {
                    "COVERAGE": "1",
                    "DEBUG": "1",
                    "DEBUG_FLAG": "-debug",
                    "DOLBY": "1",
                    "PATH": "foo",
                    "DYLD_LIBRARY_PATH": "/Users/zfields/hello/world/"
                }
            },
            "command": "echo BAZ=$BAZ; echo COVERAGE=$COVERAGE; echo DEBUG=$DEBUG; echo DEBUG_FLAG=$DEBUG_FLAG; echo DOLBY=$DOLBY; echo DYLD_LIBRARY_PATH=$DYLD_LIBRARY_PATH; echo FOO=$FOO; echo PATH=$PATH; echo pwd=$(pwd)",
            "problemMatcher": []
        },
...
    ]
}

OUTPUT:

> Executing task: echo BAZ=$BAZ; echo COVERAGE=$COVERAGE; echo DEBUG=$DEBUG; echo DEBUG_FLAG=$DEBUG_FLAG; echo DOLBY=$DOLBY; echo DYLD_LIBRARY_PATH=$DYLD_LIBRARY_PATH; echo FOO=$FOO; echo PATH=$PATH; echo pwd=$(pwd) <

BAZ=bat
COVERAGE=1
DEBUG=1
DEBUG_FLAG=-debug
DOLBY=1
DYLD_LIBRARY_PATH=
FOO=bar
PATH=/Users/zfields/.pyenv/shims:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:foo
pwd=/Users/zfields/dev/uplynk_source/uplynk/bar

Terminal will be reused by tasks, press any key to close it.

Observations:

1) changes to ~/.bash_profile are reflected immediately in the __diagnostic task
2) changes to ~/.bashrc are only reflected after a reboot in the __diagnostic task
3) foo gets appended to PATH, instead of replacing PATH
4) DYLD_LIBRARY_PATH gets destroyed (_I have tried removing it from __diagnostic altogether, but it does not change the output_)

@zfields thanks a lot for testing this. I will look into why some of the behavior is unexpected.

Regarding 2.) this is expected in the sense that things from ~/.bashrc should not influence shells started with -c (which tasks do). If you want a different behavior you need to remove

case $- in
    *i*) ;;
      *) return;;
esac

from the beginning however then exports from the ~/.bashrc will always win.

Can you let me know in terms of variables which once you want to define were and which once should win?

My expectation is as follows. If I am defining environment variables in tasks.options.env, then I would expect those environment variables to be available to the task, regardless. In other words, tasks.options.env should override everything else.

Specifically, DYLD_LIBRARY_PATH is very important to me. Several of my workflows require my company's release libraries (i.e. export DYLD_LIBRARY_PATH=/Users/zfields/lib set in ~/.bash*). However, when I'm in a development environment I will need to override the release libraries with debug versions .../debug/lib so I can step through and debug.

It would be great if I could simply override DYLD_LIBRARY_PATH in the task, so I can develop and debug without modifying my file system.

This should actually work by having the default DYLD_LIBRARY_PATH value in the ~/.bashrc and then override in the env setting of the task. I will give it a try on a Mac to verify it is indeed working.

Can this be related to Runtime Protections?

https://developer.apple.com/library/content/documentation/Security/Conceptual/System_Integrity_Protection_Guide/RuntimeProtections/RuntimeProtections.html

BTW the one mentioned by @zfields is the desired feature for most vscode users: it would be great if I could simply override DYLD_LIBRARY_PATH in the task, so I can develop and debug without modifying my file system.

In this case the easiest is for now to have a task command line that sets DYLD_LIBRARY_PATH to the desired value.

Some more digging here with our Mac expert and here is a workaround for now: in the task that wants to override the environment variable add the following:

            "options": {
                "shell": {
                    "executable": "bash",
                    "args": [
                        "-c"
                    ]
                }
            }

Let me know if this fixes the problem for you. If so I can make a change in VS Code itself that doesn't make this necessary anymore.

Task:

{
    "label": "__diagnostic",
    "type": "shell",
    "options": {
        "cwd": "${workspaceFolder}/beam",
        "env": {
            "COVERAGE": "1",
            "DEBUG": "1",
            "DEBUG_FLAG": "-debug",
            "DOLBY": "1",
            "PATH": "foo",
            "DYLD_LIBRARY_PATH": "/Users/zfields/lib/:/Users/zfields/debug/lib/"
        },
        "shell": {
            "executable": "bash",
            "args": [
                "-c"
            ]
        }
    },
    "command": "echo BAZ=$BAZ; echo COVERAGE=$COVERAGE; echo DEBUG=$DEBUG; echo DEBUG_FLAG=$DEBUG_FLAG; echo DOLBY=$DOLBY; echo DYLD_LIBRARY_PATH=$DYLD_LIBRARY_PATH; echo FOO=$FOO; echo PATH=$PATH; echo pwd=$(pwd)",
    "problemMatcher": []
}

Results in:

> Executing task: echo BAZ=$BAZ; echo COVERAGE=$COVERAGE; echo DEBUG=$DEBUG; echo DEBUG_FLAG=$DEBUG_FLAG; echo DOLBY=$DOLBY; echo DYLD_LIBRARY_PATH=$DYLD_LIBRARY_PATH; echo FOO=$FOO; echo PATH=$PATH; echo pwd=$(pwd) <

execvp(3) failed.: No such file or directory
The terminal process terminated with exit code: 1

Terminal will be reused by tasks, press any key to close it.

If I remove the shell object from the JSON, then the task runs without exception

BAZ=boz
COVERAGE=1
DEBUG=1
DEBUG_FLAG=-debug
DOLBY=1
DYLD_LIBRARY_PATH=
FOO=bar
PATH=/Users/zfields/.pyenv/shims:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:foo
pwd=/Users/zfields/dev/beam

@zfields thanks. The error comes from the fact that VS Code couldn't find bash. Can you make it an absolute path and retry.

And can you check if this then lets you replace the FOO variable. It did work for us in that setup. With this change there is no need to have a ~/.bashrc and source it from the profile as recommended in https://github.com/Microsoft/vscode/issues/47985#issuecomment-387662882

$ which bash
/bin/bash

I modified __diagnostic in tasks.json accordingly

...
                "shell": {
                    "executable": "/bin/bash",
                    "args": [
                        "-c"
                    ]
                }
...

Result:

BAZ=bat
COVERAGE=1
DEBUG=1
DEBUG_FLAG=-debug
DOLBY=1
DYLD_LIBRARY_PATH=
FOO=bar
PATH=foo
pwd=/Users/zfields/dev/beam

Observations:
1) BAZ is picked up from .bash_profile
2) FOO is picked up from .bashrc
3) I can overwrite both BAZ and FOO from __diagnostic 馃憤
4) PATH can now be overridden correctly 馃憤
5) Neither BAZ nor FOO will reflect a change in the filesystem (previously BAZ would update - now behaves like reset in bash, but not like . ~\.bash_profile)
6) DYLD_LIBRARY_PATH cannot be overridden 馃憥 馃憥 馃憥

Actually that puzzles me. You can override BAZ and FOO but not DYLD_LIBRARY_PATH. I thought you set it in .bash_profile like you set BAZ. Can you think of any different between them.

With the workaround of specifying the shell there is actually no need anymore to have a .bashrc. If I understood you correctly you didn't have one before either. So you can move everything back into .bash_profile if you want.

Just to check: does the .bashrc you have have the following snippet at the top

case $- in
    *i*) ;;
      *) return;;
esac

I double checked and match exactly on the .bashrc (I originally copy/pasted it from you earlier).

I have remerged ~/.bashrc and ~/.bash_profile. I'm rebooting now and I will let you know what happens.

--- After reboot ---

There is no difference between the way I specify DYLD_LIBRARY_PATH and BAZ. They are line items right next to each other in ~/.bash_profile and they both show up in the terminal correctly.

Still missing DYLD_LIBRARY_PATH...

In VSCode (__diagnostic):

BAZ=bat
COVERAGE=1
DEBUG=1
DEBUG_FLAG=-debug
DOLBY=1
DYLD_LIBRARY_PATH=
FOO=bar
PATH=foo
pwd=/Users/zfields/dev/uplynk_source/uplynk/beam

In VSCode (TERMINAL):

$ echo BAZ=$BAZ; echo DYLD_LIBRARY_PATH=$DYLD_LIBRARY_PATH
BAZ=bat
DYLD_LIBRARY_PATH=/Users/zfields/.lib/:

_NOTE: VSCode is now version 1.23.1, and C/C++ Extension is version 0.17.3_

If you don't override it in the env will the task print the value correctly that is specified in .bash_profile?

No, it doesn't.

Removing the override:

> Executing task: echo BAZ=$BAZ; echo COVERAGE=$COVERAGE; echo DEBUG=$DEBUG; echo DEBUG_FLAG=$DEBUG_FLAG; echo DOLBY=$DOLBY; echo DYLD_LIBRARY_PATH=$DYLD_LIBRARY_PATH; echo FOO=$FOO; echo PATH=$PATH; echo pwd=$(pwd) <

BAZ=bat
COVERAGE=1
DEBUG=1
DEBUG_FLAG=-debug
DOLBY=1
DYLD_LIBRARY_PATH=
FOO=bar
PATH=foo
pwd=/Users/zfields/dev/beam

Terminal will be reused by tasks, press any key to close it.

However, if I press "Enter" to close the task terminal and fall back to the terminal in VSCode, then I copy/paste the command string from above and get these results:

$ echo BAZ=$BAZ; echo COVERAGE=$COVERAGE; echo DEBUG=$DEBUG; echo DEBUG_FLAG=$DEBUG_FLAG; echo DOLBY=$DOLBY; echo DYLD_LIBRARY_PATH=$DYLD_LIBRARY_PATH; echo FOO=$FOO; echo PATH=$PATH;echo pwd=$(pwd)
BAZ=bat
COVERAGE=
DEBUG=
DEBUG_FLAG=
DOLBY=
DYLD_LIBRARY_PATH=/Users/zfields/.lib:
FOO=bar
PATH=/Users/zfields/.pyenv/shims:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
pwd=/Users/zfields/dev/beam

I will test this again on a Mac. I have to admit that I have no idea what makes DYLD_LIBRARY_PATH special on the Mac. However I did find articles on the web saying that you shouldn't temper with this env var and use otool -L instead. But I am not an expert when it comes to dynamic link libraries and Mac.

May not be 馃挴% related to the above, although I stumbled upon this and found options.shell recommended.
Using the options.shell within the task did not work for me to load the ~/.bashrc or ~./bash_profile.

This is what worked for me:

        {
            "label": "my-task",
            "type": "shell",
            "command": "bash --login -c 'run-command'",

Hope it helps someone else! 馃帀

Should this work the same on Windows?

I have the following task:

{
            "label": "test"
            ,"type": "shell"
            ,"options": {
                "env": {
                    "message": "hello"
                }
            }

            ,"windows": {

                "command": "echo $message"
            }
            ,"group": {
                "kind": "build",
                "isDefault": true
            }
            ,"presentation": {
                "panel": "new",
            }
}

and get the following output:

> Executing task: echo $message <


Press any key to close the terminal.

Not sure why $message is not expanded to the environment variable that is being set.

@jwmurray Please open a separate issue for your experience, as it is not directly related to this issue. This issue is still open / under investigation, and it took forever for the bug listed above to be recognized and confirmed by Microsoft. I'm sorry to shut you down, but there has been enough confusion on this thread.

@dbaeumer It has been several months since you were going to test again on your Mac, and I am curious if you have been able to land on a solution.

I will reactive my Mac and will have a look beginning of next week.

This issue and the handling of "env" options in tasks.json is kind of confusing.
I am developing on Windows and Linux (and prototyping for the latter on WSL) and adding to what @zfields reported, I have created this tasks.json.

{
    "version": "2.0.0",
    "tasks": [
        {
            "label": "check (bash)",
            "type": "shell",
            "windows": {
                "options": {
                    "shell": {
                        "executable": "bash.exe",
                        "args": [ "-c" ]
                    }
                }
            },
            "linux" : {
                "options": {
                    "shell": {
                        "executable": "bash",
                        "args": [ "-c" ]
                    }
                }
            },
            "options": {
                "env": {
                    "BAR" : "BAZ"
                },
            },
            "command": "echo env{FOO}=${env:FOO} env{BAR}=${env:BAR} var{BAR}=$BAR",
            "problemMatcher": []
        },
        {
            "label": "check (cmd)",
            "type": "shell",
            "windows": {
                "options": {
                    "shell": {
                        "executable": "cmd.exe",
                        "args" : [ "/C" ]
                    }
                }
            },
            "options": {
                "env": {
                    "BAR" : "BAZ"
                }
            },
            "command": "echo env{FOO}=${env:FOO} env{BAR}=${env:BAR} var{BAR}=%BAR%",
            "problemMatcher": []
        }
    ]
}

Results (without FOO or BAR set/exported in the parent shell that executes code)

  1. Windows cmd.exe env{FOO}= env{BAR}= var{BAR}=BAZ
  2. WSL bash.exe env{FOO}= env{BAR}= var{BAR}=
  3. Linux/Mac OS bash env{FOO}= env{BAR}= var{BAR}=BAZ

Results (with set/exported FOO=foo BAR=bar in the parent shell that executes code)

  1. Windows cmd.exe env{FOO}=foo env{BAR}=bar var{BAR}=BAZ
  2. WSL bash.exe env{FOO}=foo env{BAR}=bar var{BAR}=
  3. Linux/Mac OS bash env{FOO}=foo env{BAR}=bar var{BAR}=BAZ

I am assuming that Mac OS behaves like Linux.
_Update 2019/02/13: MacOS behaves like Linux._

Tested with Visual Studio Code 1.31.

Bottom line is:

  • ${env:...} only expands environment variables that were set in the parent shell that ran code. It doesn't expand variables set in the tasks.json env options.
  • %var% expands env variables from the tasks.json options when running cmd.exe. So does $var for real Linux bash. But on WSL, $var doesn't expand env variables from the tasks.json options.
  • %var% (cmd.exe) and $var (Linux bash) may expand to values different from ${env:var} if var was set in code's parent shell.

Is this a bug or by design??

FWIW, I would prefer if ${env:var} would also expand the env options of the tasks.json file, superseding everything that was set in the environment of code's parent shell. If ${env:var} is the standard way to expand environment (and env options) variables, the task's command may be unified across platforms because there's no need for $var vs. %var%.

If not a bug, would that be a feature request?

Since you are defining the value in the tasks.json, you already know the expanded value and you could easily just include the expanded value in the task command.
https://github.com/microsoft/vscode/issues/72323#issuecomment-487523367

This deserves reconsideration. Yes, I know the value that's going to be expanded, but I'd like to not check this value into source code.

I'd really like the ability to run tasks with arguments passed from launch.json, but pre-defining my environment or arguments in tasks.json is a non-starter for me. I need a .env or local.settings.json or equivalent file that I can read from and .gitignore.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

DovydasNavickas picture DovydasNavickas  路  3Comments

ryan-wong picture ryan-wong  路  3Comments

biij5698 picture biij5698  路  3Comments

trstringer picture trstringer  路  3Comments

chrisdias picture chrisdias  路  3Comments