5

I have a Makefile with a sshfs command followed with a cd command:

mr: # mount-remote
        sshfs -p 5022 -o nonempty myname@myssh.serveur.fr:mnt /home/julien/myworking_path
        cd ./working_directory_mwe1/wk_subd_mwe2/

The first command, sshfs works like a charm and correctly mounts both endpoint. However, the cd command does not change directory. I then have to re-type it manually. It then works if I type in manually, but I want it to be done when typing the mr command.

I tried the following instead having in mind that it may be because I have my terminal connecting for 2 seconds while executing the sshfs but that did not work either. I tried to re-run cd every second until it works.

mr: # mount-remote
        sshfs -p 5022 -o nonempty myname@myssh.serveur.fr:mnt /home/julien/myworking_path
        until cd ./working_directory_mwe1/wk_subd_mwe2/ ; do sleep 1 ; done

Thank you for your kind help :)

EDIT: I forgot to specify that the minimal working directories I tried to cd is exactly the one at the endpoint, actually, I realized my aim is rather:

mr: # mount-remote
        sshfs -p 5022 -o nonempty myname@myssh.serveur.fr:mnt /home/julien/myworking_path
        cd /home/julien/myworking_path/some_sub_dir1/som_sub_dir2
JKHA
  • 189

1 Answers1

18

However, the cd command does not change directory.

It does – but it only affects the temporary shell subprocess spawned by 'make', not the interactive shell above them.

The "current directory" is not a terminal-wide parameter – each process has its own current directory, and changes to it in child processes never propagate upwards (they can only be inherited down by new processes created after the change).

xterm
 └─ bash (interactive)
     └─ make mr
         ├─ /bin/sh -c "sshfs -p ..."
         │   └─ sshfs -p ...
         └─ /bin/sh -c "cd ..."

This means that nothing you do in the Makefile can directly affect the parent shell after make exits, and the same applies to scripts written in any other language. (Environment variables work the same way, too.)

The only way to make this work is to turn mr (that is, "mr" alone, no longer "make mr") into a shell alias or a shell function – both of which are executed internally to the shell.

Functions can be defined in your ~/.bashrc or ~/.zshrc:

mr() {
    sshfs foo:mnt ~/working_path
    cd ~/working_path/something
}

Note that you don't have to type the parenthesis when invoking shell functions, only when defining them. Once defined they behave like regular commands and are invoked as just mr. (See also other ways to define them.)

If the function is sufficiently short and simple (and in particular, if doesn't need to accept arguments through $1 etc.), then it can be an alias instead – aliases are able to change the current directory as well:

alias mr='sshfs foo:mnt ~/working_path && cd ~/working_path/something'


On top of that, regular make runs each line in a separate shell subprocess, so even further commands in the same Make recipe remain unaffected.

If you had instead wanted the cd to affect the Make recipe, you would need to either join all recipe lines into a single "logical line" using \ continuations, like this:

mr:
    sshfs foo:mnt ~/myworking_path
    cd ./working_directory_mwe1/wk_subd_mwe2/ && \
        pwd && ls && echo "This happens in the new directory..."
    pwd && ls && echo "...this is back in the old one."

...or use the GNU Make-specific .ONESHELL: special target:

.ONESHELL:

mr: sshfs foo:mnt ~/myworking_path cd ./working_directory_mwe1/wk_subd_mwe2/ pwd; ls; echo "This time the directory change did carry over."

grawity
  • 501,077