Search code examples
linuxbashunixmkdircd

bash script to create and cd to directory with spaces in name


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?


Solution

  • 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.