shellhaskelldirectoryoptparse-applicative

How to change the parent working directory from which my executable is run?


My project is a command-line application in Haskell and uses the opt-parse applicative package to handle parsing of command line options.

I am trying to implement a functionality which can change the working directory of the shell from which the command was run.

For example, I want to do something like

$ program foo

and have the shell change to a specified directory my program has associated with the passed option foo.

$ program foo
input 'foo'; now changing to directory '~/mydirectories/foodirectory'
[~/mydirectories/foodirectory]$

I attempted to implement this functionality using the setCurrentDirectory function from the directory package, however this does not appear to affect the working directory in the shell from which the directory is run; no change in the working directory appears. I am imagining that it is setting some kind of internal-to-the-program working directory, as I see no change in the shell from which the executable was run.

Is it possible to have my program change directories in this manner? Such directory-switching functionality would improve the convenience of using my application.

How can I implement directory-switching functionality from a command-line executable?


Solution

  • In general, you can't do this. No program can change its parent process's directory, no matter what language it's written in, using standard UNIX interfaces. A program can only change its own directory (and, by extension, the initial working directory of any programs it subsequently starts).

    However, with the shell's active participation, you can make something very similar. Consider instructing your users to run a shell function such as the following:

    # this wrapper works on any POSIX shell, but will avoid setting a global variable "dir" if
    # the active shell (like bash, ksh, or even dash) supports local variables.
    chdirWithMyHaskellProgram() {
        local dir 2>/dev/null || \
          declare dir 2>/dev/null || \
          : "current shell does not support local variables; proceeding anyhow"
        dir="$(myHaskellProgram "$@")" && cd -- "$dir"
    }
    

    ...will cause chdirWithMyHaskellProgram --foo-bar to call myHaskelProgram --foo-bar, and change to the directory named in the output of same, if and only if the exit status indicates success.


    Consider having myHaskelProgram --eval emit the source code to the above function. If you implemented this, then your user would need to put eval "$(myHaskelProgram --eval)" in their dotfiles, but would thereafter have the desired functionality.