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?

  • 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 '16 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. – Captain Man Jan 20 '16 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 '16 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? – Captain Man Jan 20 '16 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 '16 at 18:25
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).

  • Somehow this works... Weird. I'd really rather not have to do an exit everytime after I run this. (Note: I didn't downvote) – Captain Man Jan 20 '16 at 18:17
  • Change it to exec bash – Jeff Schaller Jan 21 '16 at 1:11
  • Is there a way to make this shell agnostic? – Captain Man Mar 7 '16 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 '16 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).

  • 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. – Captain Man Jan 26 '16 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 '16 at 22:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.