2

I am using fish and am working on a project with a script that's frequently invoked for build tasks called x.py. I'd like to create an alias that maps x to ./x.py. However, I only want this to apply inside that specific directory.

The fish documentation gives a fairly detailed explanation on how to make various kinds of aliases/functions/etc, but I can't find anything on how to make them directory specific.

Any help would be much appreciated

2
  • In general, not knowing fish, you should write your own cd function that does what cd does (handles "cd ~" and "cd", does the actual "cd"), then decides to "alias" or "unalias", based on the directory name or the existence of a file in the destination directory.
    – waltinator
    Commented Oct 17, 2021 at 3:55
  • 2
    You would probably need a function to check current directory and then do what you want depending on directory. It may be possible to do with an alias if your mind is really set on using an alias, but functions are way more flexible and powerful.
    – svin83
    Commented Oct 17, 2021 at 5:07

4 Answers 4

3

Three possibilities that I can think of (other than the ones that Damir suggested). First, if at all possible, I'd just test whether or not you are in the directory in a "lazy-load" function. This is similar to what Damir recommended, but it avoids the overhead of being in your startup config or in an executable script.

Lazy-load function-based solution

Create ~/.config/fish/functions/x.fish:

function x
    if [ (pwd) = "/path/to/project` ]
        ./x.py
    else
        command x
    end
end

This function will only load when called for the first time via x. This avoids the overhead of adding it to your startup config. It is also still a function, so it executes in the current fish shell, rather than starting a new shell like an executable script would.

It also falls back to any other x command that might be installed on the system in case you aren't in that directory. If you don't need this, just delete the else block.

Function which is created when you enter the project directory

If you really need to have the function only exist when you are in that directory, there are two more alternatives. First, fish functions can watch for a variable to change, and only run when it does. That variable can be $PWD to watch for a directory change.

Add the following in ~/.config/fish/conf.d/create_x.fish:

function create_x --on-variable PWD
    if [ "$PWD" = "/path/to/project" ]
        function x
            ./x.py
        end
    else
        functions --erase x
    end
end

This does require the create_x.fish function to be loaded at startup, but it will only run when you change directories. It's definitely less efficient than the first option.

Prompt-based function

Finally, you can modify your prompt function to check the current directory. This seems wasteful, but:

  • funced fish_prompt

  • Add the following to the bottom:

    functions --erase x
    if [ (pwd) = "/path/to/project" ]
        function x
            ./x.py
        end
    end
    
  • funcsave fish_prompt

This will check to see if you are in the project directory on each prompt, and will only create the function if you are.

The funcsave places a copy of the "normal" fish_prompt in your ~/.config/fish/functions directory. Delete it to return to the normal prompt functionality.

Definitely, go with the first option if you can :-)

3
  • The first solution seemed the most simple and obvious, but it only works once in a new shell. I suppose this is normal, fish probably just tries to define a function once then ignores the file. Isn't a function declaration missing in the then branch?
    – vctls
    Commented May 19, 2023 at 9:40
  • @vctls You are absolutely correct - My bad! Thanks for pointing that out. If you'd like to make the edit, I'll test it and accept. The function declaration needs to wrap the entire thing, I believe. Otherwise, it would just create the function if you were in that directory when the shell started. Commented May 19, 2023 at 10:49
  • That's right. I suppose the most logical behavior is to have it defined the same way whatever the initial pwd is.
    – vctls
    Commented May 19, 2023 at 10:53
1

Finally, someone else who wants this!

If you're still interested I have a little project called "Dangerload" that I use to have functions dynamically loaded/unloaded based off of the directory you're in. You can copy this file into your ./config/fish/functions. Or just run fisher install redaphid/dangerload-fish

It also will check if the function has been changed on disk and automatically update the function before exection.

It's like direnv, but for fish functions.

As the name implies, this has extreme implications if you don't trust the source of the folder you're currently in. Because of this, you have to type 'dls' in once per terminal session to opt in to this behavior. haha.

I had a fancier version once, but this one just looks at a the file ./scripts/dangerload.fish and pulls the functions you want to add/remove from your shell from there. cd'ing out of the directory erases those functions.

I've been excited about this idea for a long time now, as you can have a domain-specific language in your shell scoped to the folder you're currently in. I have made shell scripts for projects that you can either execute to perform a task, or use in dangerload to pull all the functions out of said shell script. Hit me up on Github if you/anyone feels like they'd get value out of this. I have better stuff that does this.

0

I think this is not possible to do with alias (if alias used solely).

Solutions:

  • create the function inside ~/.config/fish/config.fish

      function x
        set desired_dir "/home/student/test_x" # Set your absolute path to your desired directory
        if test (pwd) = $desired_dir
          ./x.py
        else
          echo '"Alias" x :) is not allowed here! You are not in $desired_dir directory.'
        end
      end
    

    If you don't have config.fish file, create it with this code above in it.

  • create the script/file with the name x under ~/bin directory with this code:

    #!/usr/bin/fish
    
    set desired_dir "/home/student/test_x" # Set your absolute path to your desired directory
    if test (pwd) = $desired_dir
      ./x.py
    else
      echo '"Alias" x :) is not allowed here! You are not in $desired_dir directory.'
    end
    

    Do not forget to make your x file executable:

    chmod u+x ~/bin/x
    
1
  • A good attempt, but I would recommend a lazy-load fish function over either of these two approaches. Definitely recommend against putting function definitions in your config.fish. That's the bash way of doing things :-). And a script runs in a new process, so it's less efficient than a function. Commented Oct 17, 2021 at 5:17
0

I Would suggest using direnv, then hook direnv into fish to set a custom variable depending on the folder.

Then, use that variable in your fish function.

direnv is an extension for your shell. It augments existing shells with a new feature that can load and unload environment variables depending on the current directory.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .