0

This question already has an answer here:

I made this script:

#!/bin/bash

if [[ $# -ne 1 ]]; then
  echo "only use one argument"
  exit 1
fi

mkdir $1
sleep 0.5
cd $1

exit 0

I wanted to make a script that I would later put into /usr/bin, such that whenever I write crdir asd it creates the directory and automatically changes my current directory to the new directory.

This way doesn't work, because the schell script is apparently a subshell, and the cd command will not take effect outside of the subshell, so how would I do this?

I read online that I should "source" it, run it with a dot in front, like . ./crdir asd, but first of all, when I tried this, ssh got disconnected from the machine, and secondly, this wouldn't work the way I wanted it to work, as a command.

Any tips?

EDIT: I also tried mkdir aaa; cd !# but it didn't work, it took !# as the argument from the previous command.

marked as duplicate by andcoz, Kusalananda bash Aug 28 '18 at 15:25

This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.

4

Make it a function instead:

crdir() {
  if [ "$#" -eq 1 ]; then
    mkdir -p -- "$1" && cd -P -- "$1"
  else
    echo >&2 "Usage: crdir <directory-path>"
    return 1
  fi
}

Which you can put in your ~/.bashrc.

Your . crdir approach would also work (in bash and other shells whose . can take arguments), but you'd need to remove that exit 0 which causes the shell to exit. Also note that when not in POSIX mode, bash looks for the file to source in the current directory if it can't be found in $PATH, which makes it a bit dangerous (at the same level as adding . or an empty entry at the end of $PATH).

Also if . crdir is called without arguments, then the script will inherit the positional parameter of the shell. For instance, set foo; . crdir would not return an error and would create the foo directory.

A few other notes on your script:

#!/bin/bash

Note that the she-bang is ignored (is just seen as a comment) when the file is sourced.

if [[ $# -ne 1 ]]; then
  echo "only use one argument"

It's preferable to output error messages on stderr (echo >&2 text).

  exit 1
fi

mkdir $1

Leaving parameter expansions unquoted has a special meaning in bash, you should almost never do that.

Also, if $1 starts with a -, it will be treated as an option by mkdir. You'd need to use the -- end-of-option marker to tell mkdir the content of $1 is not an option even if it starts with -.

You're also not checking if the mkdir command succeeded.

You may also want to use the -p option which tells mkdir to create all the directory components and also causes mkdir not to return failure if the directory already existed. So:

 mkdir -p -- "$1" &&
sleep 0.5

No need to sleep. You can be sure that the directory has been created when mkdir returns successfully.

cd $1

Again, you've got the quoting and - issues. Also, in bash you need the -P option for cd to interpret paths the same way as other utilities like mkdir. So:

 cd -P -- "$1"

(ignoring for now a path called - or $CDPATH which I'd hope you don't use or use only with sane values).

exit 0

Again, you're not checking the exit status of cd and returning with a success exit status, even if mkdir or cd failed. When a script is sourced, it's interpreted by the sourcing shell, so that exit would cause it to exit.

Here, I'd rewrite the script as:

if [ "$#" -eq 1 ]; then
  mkdir -p -- "$1" && cd -P -- "$1"
else
  ! echo >&2 "Usage: crdir  <directory-path>"
fi
  • What are the double dashes in mkdir -p -- "$1"? Also the lack of exit status checks is because I just wanted to see how to make it work first. – iamAguest Aug 28 '18 at 14:46
  • @iamAguest, see edit. – Stéphane Chazelas Aug 28 '18 at 14:50
0

How about a function:

crdir() { mkdir $1; cd $1; }

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