Search code examples
fish

Eval in fish shell function working strange


I tried to make wrapper to docker's command to change docker machine context. Here's the command and output:

docker-machine env dev

set -x DOCKER_TLS_VERIFY "1";
set -x DOCKER_HOST "tcp://192.168.99.101:2376";
set -x DOCKER_CERT_PATH "/Users/sandric/.docker/machine/machines/dev";
set -x DOCKER_MACHINE_NAME "dev";
# Run this command to configure your shell:
# eval (docker-machine env dev)

Code for function, I placed in .config/fish/config.fish:

function cdm
  eval (docker-machine env $argv)
end

So, when trying to run cdm in new session, function evaluated, but context didn't change. If however, I'd run:

eval (docker-machine env default)

from command prompt, and tried to run cdm with different arguments - all works fine. So I suspect that it has smth to do with existing of environment variables this command trying to set from fish function.. I even tried then to experiment and change that function to alias (which is also alias to fish functions as I get) command with constant instead of parameter:

alias cdm "eval (docker-machine env dev)"

And it worked the same - it didn't changed environment variables if I ran this alias first on newly opened session, but if I run eval code from command prompt - after that alias working as expected.

So, whats its all about, anyone have any ideas?


Solution

  • It looks like that Docker's output does not specify an explicit scope, so when you run it inside your function and those variables have not been defined elsewhere, they will end up in the function's scope.

    However, if you run the same code from the command prompt, you will end up with variables defined in the global scope, which are then updated by the set in the function.

    See the documentation for set:

    The scoping rules when creating or updating a variable are:

    1. If a variable is explicitly set to either universal, global or local, that setting will be honored. If a variable of the same name exists in a different scope, that variable will not be changed.

    2. If a variable is not explicitly set to be either universal, global or local, but has been previously defined, the previous variable scope is used.

    3. If a variable is not explicitly set to be either universal, global or local and has never before been defined, the variable will be local to the currently executing function. Note that this is different from using the -l or --local flag. If one of those flags is used, the variable will be local to the most inner currently executing block, while without these the variable will be local to the function. If no function is executing, the variable will be global.

    To fix this problem, try defining your cdm function with --no-scope-shadowing (although this seems to work, I'm not sure that it should).