4

I'm working on a project to remotely create a repository in my GitLab CE instance (that part is working!) and then to create a directory using the name of the project (working!) and cd into that directory (here's where I have the issue...) and then to initialize locally and add the remote repository (working!!!)

The issue I'm having is solely changing into the new directory. Whether I use the code shown below, or simply try to cd "$1" or cd "$*" I can't seem to get it to work at all!

#!/bin/bash

dir="$*"
wd=$(pwd)
fulldir="$(pwd)/${dir// /\\ }/"
echo "Creating directory $dir"
mkdir -v "$dir"
cd "$dir"
echo "Changing current directory to $dir"
echo $dir
echo $fulldir

The output of this code is:

root@cana:~# ls
glnewproj  test
root@cana:~# bash test Hello World
Creating directory Hello World
mkdir: created directory 'Hello World'
Changing current directory to Hello World
Hello World
/root/Hello\ World/
root@cana:~# ls
Hello World  glnewproj  test
root@cana:~# pwd
/root

How can I cd into my newly created directory? I'm totally stumped.

Edit:

Typed up the function per ghoti and tested it within .bashrc and within my test script.

When running the function directly from bash:

root@cana:~# ls
glnewproj  test  test2
root@cana:~# mkcd "Hello World"
root@cana:~/Hello World# 

When running the function from within a new test script:

root@cana:~# ls
glnewproj  test  test2
root@cana:~# cat test2
#!/bin/bash

mkcd() {
  mkdir -p "$1" && cd "$1"
}

mkcd "$1"

root@cana:~# bash test2 "Hello World"
root@cana:~# ls
Hello World  glnewproj  test  test2

So the script is still running as a child and thus not updating the parent shell's current directory. Is my only option to spawn a new shell at the end of the script?

4
  • Your test2 script is still a separate script, and is not running within your interactive shell. The fact that test2 internally has its own function called mkcd doesn't change the fact that it's running in a subshell that you created by running bash in your interactive shell. Instead of running `bash test2 "Hello World", make test2 a function within your running shell, and run the function. Commented Apr 3, 2016 at 4:26
  • What I'm trying to do entirely depends on it being a script, however. Commented Apr 3, 2016 at 4:35
  • Well, you have multiple answers now, including your own, and you understand why you're having the problem you're having. If you really want to be able to change directory in your interactive shell to something that is created by your script, there are multiple ways of hacking that together, and I've described one in my answer. Whether it's applicable to you is something I can't possibly know given the extent of the detail in your question. Commented Apr 3, 2016 at 4:39
  • I am lazy if I can be, and I have this in my bash aliases (→here, cd's will remain effective beyond “script” ending): function mkcd(){ /usr/bin/mkdir -p "$*" builtin cd "$*" }. Thus I can in all lazyness and without any quotes go about it and say: mkcd network testing Commented Oct 3, 2021 at 12:05

3 Answers 3

6

When you cd from within the script, your directory is changed within the script, but not within the calling shell. The working directory in your interactive shell is set by cd commands in THAT shell, not inside programs that are run by it.

If you want your script to be able to change the directory of your interactive shell, then you'll probably need to set up a bash function. For example, the following could be added to your .bash_profile:

mkcd() {
  mkdir -p "$1" && cd "$1"
}

Since this function runs within the context of your interactive shell, rather than spawning a subshell or child process, the directory changes it does are retained after it exits.


Another option, if you need/want to make your directory creation script a standalone tool, is to model things after mktemp, which creates what it needs to, and then returns the name of what it created to stdout. Thus, with the following script, perhaps called mkcd:

#!/bin/bash

if mkdir -p "$*"; then
  cd -P "$*"
  pwd
  r=0
else
  r=$?
fi

exit $r

Then in your calling script, you could run this like:

#!/bin/bash

d=$(mkcd Hello World)

if [ ! -d "$d" ]; then
  echo "ERROR: I can't go on like this" >&2
  exit 1
fi

cd "$d"
: do your stuff

Note that the error checking remains a good idea even if it seems redundant, as one could fall victim to accidental or intentional unpredictable behaviour if special characters or odd formatting were used to build the directory name.

Sign up to request clarification or add additional context in comments.

6 Comments

So this should be placed in .bashrc yes? And then when run from the script, it should function outside of the script and I can avoid spawning an alarming amount of sessions per my answer. :P
In your .bashrc will work, or your .bash_profile if this is for use interactively. For use within a script that you, for example, run from cron, you can just add it to the top of the script. I'm not sure of your use case, we might be making this an XY problem...
I'm running into some weird behavior with this. I've added it to .bashrc and get the desired result if I run the function directly from the shell, but if I run it as a script, the script is still executing it and not affecting the parent shell.
How are you calling it from within your script? Perhaps you could update your question with these experiments and your results.
Updated the question with some more info.
|
2

With a bit more research, I've discovered that this is because the script is a child process and cannot effect the parent shell. That is to say, the script will cd for its own purposes. I could, for example, change the code to the following:

#!/bin/bash

dir=$1
mkdir -v "$dir"
cd "$dir"
touch test.file

And run

$ bash test "Hello World"

And the result would be the file test.file appearing in ./Hello\ World/, however when the script finishes, I will still be in the previous working directory.

A sloppy way of getting the result I wanted, which is to cd into the new directory, is to spawn a new session within the script using exec bash.

Comments

-1

Your script is (mostly) fine, as long as you use cd "$1" as you mentioned in the question. The problem is that you need to make sure the full directory name is actually passed as a single argument.

$ bash test "Hello World"

Then in your script

# Omitting unused $wd
# Omitting $fulldir, which appeared to be for debugging purposes only
dir=$1
mkdir -v "$dir"
cd "$dir"

1 Comment

Unfortunately this doesn't change the directory for me either.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.