go exec "cd" command in current terminal window

1,785 views
Skip to first unread message

Keith Rosenberg

unread,
Sep 19, 2013, 8:32:58 PM9/19/13
to golan...@googlegroups.com
Hi folks,

New to the group, and I've been learning go mainly through the pkg files in source (super well documented) with some other awesome resources on the web as well. I've gotten to a point with my first program though where I think I need to reach out to folks in the know.

I have a go program that behaves much like a script I would normally write in bash or node, but when the program is done executing, I want the result to be that I have "cd"ed into a new directory (which I locate via my go procedures). However, with the os.Exec and syscall.Exec methods, I am finding that things like "ls" will work fine and list the directory, but "cd" will do nothing. It will not give me an error, because, I imagine what is happening is that the program context *is* "cd"ing into the correct directory, and then exiting back into the directory I was in.

It's a very simple program in concept but it's a long procedure, but in psuedo-code:

1. Parse command line flag for directory name (Check)
2. Parse environment (os.Environs()) variables for a config path/root path for this program (Check)
3. Verify that there is a directory with flag name pointer (Check)
4. Leave the user who ran the program in that directory in their terminal window

It's step four that I cannot quite figure out. Is what I am trying to do possible, or is this really something I should just use bash for? It's my first go program so I am really looking to make something useful I can be proud of :)

Thanks a ton,
- Keith

Ian Lance Taylor

unread,
Sep 19, 2013, 11:25:37 PM9/19/13
to Keith Rosenberg, golang-nuts
On Unix like systems the current directory is a property of the
process. When you start a new process, it can change its own
directory, but it can not change the directory of its parent process.
The only way to do this kind of thing is to write a shell script that
your shell interprets directly, without starting a subprocess; you
can't do it using a Go program.

Ian

Keith Rosenberg

unread,
Sep 20, 2013, 12:39:24 AM9/20/13
to golan...@googlegroups.com, Keith Rosenberg
Thanks Ian, this certainly makes sense and is what I imagined to be the case. If I were to create an independent bash script that did that part of it (simple cd to variable dir name), and try to exec that script via Go, would that achieve this or would it really still be the same problem?

It would be great if there was like an "exit and do callback" mechanism or something, like, process all these procedures and then Exit(3, func(){ /* cd to dir */ }) but it would be strange that you would tell a program to terminate and then do something.

brainman

unread,
Sep 20, 2013, 12:42:42 AM9/20/13
to golan...@googlegroups.com, Keith Rosenberg
On Friday, 20 September 2013 14:39:24 UTC+10, Keith Rosenberg wrote:
... If I were to create an independent bash script ...

go program cannot execute bash scripts, you must use bash program for that. And you need to start new bash process for that. So you still have same problem.

Alex

chris dollin

unread,
Sep 20, 2013, 1:44:52 AM9/20/13
to Keith Rosenberg, golang-nuts
On 20 September 2013 05:39, Keith Rosenberg <kthros...@gmail.com> wrote:
Thanks Ian, this certainly makes sense and is what I imagined to be the case. If I were to create an independent bash script that did that part of it (simple cd to variable dir name), and try to exec that script via Go, would that achieve this or would it really still be the same problem?

Exactly the same problem.

The usual trick is to do something like

    cd $(runMtGoProgram and its arguments)

so that it's your existing Bash shell that executes the cd and the
program just computes the desired directory and writes it to stdout.

It would be great if there was like an "exit and do callback" mechanism or something, like, process all these procedures and then Exit(3, func(){ /* cd to dir */ }) but it would be strange that you would tell a program to terminate and then do something.

That's almost what happens above ...

Chris

--
Chris "allusive" Dollin

Keith Rosenberg

unread,
Sep 20, 2013, 8:32:06 AM9/20/13
to golan...@googlegroups.com, Keith Rosenberg, ehog....@googlemail.com
Right then, thanks much folks - I am a big advocate of using the right tool for the right job so I will get to writing a different project in Go 

Diddymus

unread,
Sep 20, 2013, 12:10:35 PM9/20/13
to golan...@googlegroups.com, Keith Rosenberg
Hi Keith,

Actually you can do this with bash. In your ~/.bashrc define a function that calls your go program and have the go program return the calculated path on stdout:

function godir() {
  cd "`~/goprogram`"
}

Here godir is your new command to use on the command line which calls goprogram and changes to the returned directory. No sub process are called and godir will change your current directory for you.

Keith Rosenberg

unread,
Sep 20, 2013, 2:13:57 PM9/20/13
to golan...@googlegroups.com, Keith Rosenberg
Brilliant! That's very cool Diddymus thanks for the problem solve, love it

John Nagle

unread,
Sep 20, 2013, 5:35:43 PM9/20/13
to golan...@googlegroups.com
On 9/19/2013 9:39 PM, Keith Rosenberg wrote:
> It would be great if there was like an "exit and do callback" mechanism or
> something, like, process all these procedures and then Exit(3, func(){ /*
> cd to dir */ }) but it would be strange that you would tell a program to
> terminate and then do something.

Although it's not relevant to the original poster's problem, it's
worth nothing that asymmetry in UNIX/Linux. When you invoke a program,
you get to pass an array of parameters, the command line args, and a
collection of name-value pairs, the environment variables. It's like
a function call with positional parameters and variables imported
from an outer scope.

But when a process exits, all it sends back is one integer value,
the return code. "Exit" doesn't have parameters like "argc" or
"argv", and there's no way to return or affect environment variables.
This is a lack. It's one of the things that tends to lead to rather
"blind" shell scripts, makefiles, and GUIs, where the controlling
process has very limited info about what the subprocess did.
A common Unix/Linux GUI element is something that does something
using a command-line tool based on GUI actions, but displays what
happened to the user as a text stream in a scroll box. That bit of
inelegance derives from the one-way model of subprocess communication.

I know why it was done that way. It's a quirk of the PDP-11 UNIX
implementation of process launch. That's what also produced the rather
strange fork/exec approach to starting a subprocess. Fork was
originally implemented by swapping out the program to disk, then, rather
than releasing the memory of the in-memory copy, the process table
entry was duplicated, with one entry referring to the in-memory
process and one referring to the swapped-out process. In that
model, there was no way to get variable-length data back.

This bit of legacy is so deeply embedded in the minds of UNIX/Linux
programmers that they don't even notice it.

John Nagle


John Nagle

unread,
Sep 20, 2013, 5:37:55 PM9/20/13
to golan...@googlegroups.com
On 9/20/2013 2:35 PM, John Nagle wrote:

> Although it's not relevant to the original poster's problem, it's
> worth nothing that asymmetry in UNIX/Linux.
noting

John Nagle

netpoetica

unread,
Sep 20, 2013, 6:38:27 PM9/20/13
to golan...@googlegroups.com, na...@animats.com
I'm not super familiar with the implementation behind UNIX/Linux, but it does sound like what you're saying this possibility (Exit(int code, func callback)) is limited from happening by some old UNIX decisions, and then unless another OS was forged redefining the exit() function, this won't be happening any time soon.

I had always thought that Exit() was an implementation of the langauge, not the OS, but again I haven't been deep in any OS source. I was looking through the plan9 source for the heck of it after you mentioned this (http://plan9.bell-labs.com/sources/plan9/sys/src/) (for anyone reading who hasn't seen Go source much, plan9 is some of the Go creators previous project, a lot of the source for go comes there there) but I couldn't find an implementation of the sys Exit() function (the definition). Again, my C skills aren't too sharp and I haven't poked around much in any OS source. Couldn't find anything so I checked UNIX kernel source and also couldn't find the definition of Exit.

Would love to get a glimpse at the UNIX definition of the exit function (sys exit) to see what it looks like, if anyone knows where to find it

Ian Lance Taylor

unread,
Sep 20, 2013, 6:46:38 PM9/20/13
to netpoetica, golang-nuts, John Nagle
On Fri, Sep 20, 2013 at 3:38 PM, netpoetica <kthros...@gmail.com> wrote:
>
> Would love to get a glimpse at the UNIX definition of the exit function (sys
> exit) to see what it looks like, if anyone knows where to find it

For background, what John is talking about is, in C, called _exit.
The exit library call does stuff like call all atexit functions and
flush all stdio buffers, and then calls _exit.

On a GNU/Linux system, _exit is a system call. The source code in the
C library, which is not too enlightening, may be seen at
https://sourceware.org/git/?p=glibc.git;a=blob_plain;f=sysdeps/unix/sysv/linux/_exit.c;hb=HEAD

The source code in the Linux kernel may be seen at

https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/kernel/exit.c?id=refs/tags/v3.12-rc1

Search for SYSCALL_DEFINE1(exit, int, error_code).

Ian

netpoetica

unread,
Sep 20, 2013, 8:48:19 PM9/20/13
to golan...@googlegroups.com, netpoetica, John Nagle
Nice! Thanks Ian! Looks like for GNU the idea of a callback would be completely unfeasible as it only accepts an integer, but for the Linux Kernel that struct contains the signal integer and could easily contain some sort of function that might be able to get picked up by bash via std output or something. But I certainly will not be attempting to rewrite Linux any time soon :-) thanks for pointing me to that source though, it's a nice place to look to see how others are writing much more complex C

FWIW I did manage to use bash to execute my cd command by making Go output to stdout via

          fmt.Fprint(os.Stdout, pathToProjects + "/" + *targetDirectory)

and in case anyone comes to this thread with a similar question, I did put the solution up on Github: https://github.com/netpoetica/pj/blob/master/pj.go

The part about how to whip up that quick bash stuff in your bash/zsh rc file is in the README

Thanks a bunch folks

Jan Mercl

unread,
Sep 21, 2013, 3:59:50 AM9/21/13
to John Nagle, golang-nuts
On Fri, Sep 20, 2013 at 11:35 PM, John Nagle <na...@animats.com> wrote:
> This bit of legacy is so deeply embedded in the minds of UNIX/Linux
> programmers that they don't even notice it.

I disagree vehemently. IMHO it's not an artifact but an intentional
design choice. How would your hypothetical unlimited number of return
values from a process fit the Unix pipe model?

-j

John Nagle

unread,
Sep 21, 2013, 1:19:24 PM9/21/13
to golan...@googlegroups.com
ls | mc

is obsolete.

John Nagle


Benny Siegert

unread,
Sep 21, 2013, 4:52:02 PM9/21/13
to Jan Mercl, John Nagle, golang-nuts
On Sat, Sep 21, 2013 at 9:59 AM, Jan Mercl <0xj...@gmail.com> wrote:
> I disagree vehemently. IMHO it's not an artifact but an intentional
> design choice. How would your hypothetical unlimited number of return
> values from a process fit the Unix pipe model?

Well, in Inferno and Plan9 (IIRC), the return value is a string.

roger peppe

unread,
Sep 21, 2013, 9:28:59 PM9/21/13
to John Nagle, golang-nuts
On 21 September 2013 18:19, John Nagle <na...@animats.com> wrote:
> ls | mc
>
> is obsolete.

Surely not!

Personally, I still find shell pipelines to be greatly inspiring. They
show that it really is possible to plumb mutually incognizant tools
together and have them do useful work.
Reply all
Reply to author
Forward
0 new messages