3

I know what I can make a shell procedure or source the script instead of running it to cd the shell I am using. What I want to know is there any way to cd the "parent shell" (not sure if that is the correct phrase).

#!/bin/sh
# This is script.sh
cd $1
pwd

Here's some output. (Note: this isn't the exact output, I just shortened it by stripping out unnecessary details. Lines that are my input into the shell begin with >.)

> mkdir foo
> ./script.sh foo
/home/myName/foo
> pwd
/home/myName

This is expected behavior as I understand. Here is if I "source" the script.

> . ./script.sh foo
/home/myName/foo
> pwd
/home/myName/foo

Without sourcing the script and without making this a shell procedure, is there any way to type ./script.sh foo result in the shell I am using be in the directory /home/myName/foo?

5
  • Why/how are you getting the > $PS2 prompt there? In any case, no. You cannot change the parent's current working directory from the child context, and the parent calls a child shell to read the script. When you source the script instead the parent reads it rather than calling the child and so changes its own directory. If you want the parent to CD in response to some message or signal from the child you'll have to construct the format yourself.
    – mikeserv
    Jan 20, 2016 at 17:11
  • @mikeserv I just shortened the output, that's not a straight copy and paste. I will make a note in the question in case it's relevant. Also, what do you mean by "construct the format yourself"? I'm pretty inexperienced with bash and not sure what you're getting at. Jan 20, 2016 at 18:11
  • Basically you'll need to talk between the processes with a pipe or kill signal or whatever. So you'll need some to construct some means of passing information to and from and the protocol is all yours to develop. Its doable, but its not done.
    – mikeserv
    Jan 20, 2016 at 18:13
  • @mikeserv That sounds pretty involved. Wouldn't that mean making a custom bash that could handle this protocol making the script not portable? Jan 20, 2016 at 18:23
  • not as such.. Just reading, writing, and testing value of strings written/read. there is an example already posted, in fact
    – mikeserv
    Jan 20, 2016 at 18:25

4 Answers 4

2

No, because when you invoke a script as ./script.sh foo, it starts a subshell to run the script in for you. When that subshell exits, you're placed back in to the directory you were in when you started that script. You can counterfeit this behavior, though, by using a script that does something like this:

#!/bin/bash
cd $1
bash

This will put you into a subshell after changing directories, which you then have to exit to get back to your original shell.

If you instead make the last line:

exec bash

You will not have to exit the new shell, because the new shell will replace the old shell. Doing this does, however, remain an ugly hack (IMHO).

4
  • Somehow this works... Weird. I'd really rather not have to do an exit everytime after I run this. (Note: I didn't downvote) Jan 20, 2016 at 18:17
  • Change it to exec bash
    – Jeff Schaller
    Jan 21, 2016 at 1:11
  • Is there a way to make this shell agnostic? Mar 7, 2016 at 18:18
  • @Captain Man: instead of bash use $SHELL. Btw the problem with this solution is that it spawn a subshell every time the script is executed, so you may end up wasting a lot of memory.
    – eadmaster
    May 21, 2016 at 2:44
2

The simple answer is no - that's impossible.

But, if you are desperate, you may find some workarounds.

The easiest approach is when there are only a few directories which you know in advance (in the parent shell) that the script may try to cd into. In such case you can set a trap for some signals which would be send from the script.

~ $ cat script
#!/bin/bash
kill -s SIGINT "$PPID"

~ $ trap 'cd /tmp' SIGINT
~ $ ./script
/tmp $

A somehow more sophisticated way of doing this is to write the command from the script on the parent shell standard input. The procedure (quite complicated) is described here, but the simplest case (with defined directory in advance) is again quite easy:

~ $ cat script
#!/bin/bash
printf '\e[5n' >/proc/"$PPID"/fd/0

~ $ bind '"\e[0n": "cd /tmp\n"'
~ $ ./script
/tmp $

If you want to pass a directory name as a parameter from the script to parent shell I suggest to study answers from above link.

1

You can try:

gdb --batch -ex 'call chdir("/some/dir")' -p "$PPID"

But I'd expect most shells to get confused when their current directory changes under their feet.

By the way, in shell syntax, you need quotes around variables and -- to separate options from arguments. See also the special behaviour of cd when not passed -P. So your code should be more like:

#! /bin/sh -
cd -P -- "$1"

To be equivalent to int main(int argc, char*argv[]){chdir(argv[1])} in C for instance (would still be ineffective at changing the current directory of another process)

0

A script is run in a subshell, any environment changes (directory and variables) are local to that process, and revert to the calling process' ones when the process exits (or otherwise hands control back).

You can run commands in the current shell (so their effect stays) via sourcing the script (. here/is/the/script) or by defining a shell function. Shell functions can be quite complex, and, crucially, are executed in the current shell process. Not all shells support functions (bash does).

2
  • I literally asked how to do this without shell functions and sourcing the script, I even gave an example in my question showing that it works if I source the script. Jan 26, 2016 at 22:53
  • @CaptainMan, then the answer is just a resounding no. The rest of the answer is for the benefit of those who want alternatives.
    – vonbrand
    Jan 26, 2016 at 22:58

You must log in to answer this question.

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