0

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?

Chris Stryczynski
  • 30,145
  • 48
  • 175
  • 286
mherzl
  • 5,624
  • 6
  • 34
  • 75
  • 3
    Not a shell limitation *or* a Haskell limitation -- you couldn't write a program in C that changed the directory of a separate C program that launched it, either, without the parent process's active participation, or using debugger interfaces or the like. – Charles Duffy Jan 25 '18 at 00:01
  • @CharlesDuffy I see, thanks. It seems that instead of changing directories I'll have to print out the path so the user can copy and `cd` to it. Out of curiosity, how does the `cd` program command work to change directories? – mherzl Jan 25 '18 at 00:03
  • 1
    `cd` is not a separate program, it's a shell builtin. `/usr/bin/cd` may exist on your OS, but doesn't actually *work* to change directories in the shell that invokes it. – Charles Duffy Jan 25 '18 at 00:04
  • `cd` a shell builtin, alright. – mherzl Jan 25 '18 at 00:05

1 Answers1

4

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.

Charles Duffy
  • 280,126
  • 43
  • 390
  • 441