277

I find myself repeating a lot of:

mkdir longtitleproject
cd longtitleproject

Is there a way of doing it in one line without repeating the directory name? I'm on bash here.

AdminBee
  • 22,803

13 Answers13

289

This is the one-liner that you need. No other config needed:

mkdir longtitleproject && cd $_

The $_ variable, in bash, is the last argument given to the previous command. In this case, the name of the directory you just created. As explained in man bash:

_         At  shell  startup,  set to the absolute pathname used to invoke
          the shell or shell script being executed as passed in the  envi‐
          ronment  or  argument  list.   Subsequently, expands to the last
          argument to the previous command, after expansion.  Also set  to
          the  full  pathname  used  to  invoke  each command executed and
          placed in the environment exported to that command.  When check‐
          ing  mail,  this  parameter holds the name of the mail file cur‐
          rently being checked."$_" is the last argument of the previous command.

Use cd $_ to retrieve the last argument of the previous command instead of cd !$ because cd !$ gives the last argument of previous command in the shell history:

cd ~/
mkdir folder && cd !$

you end up home (or ~/ )

cd ~/
mkdir newfolder && cd $_

you end up in newfolder under home !! ( or ~/newfolder )

Jeff Schaller
  • 67,283
  • 35
  • 116
  • 255
  • 1
    @JSmyth I agree, this is a one-liner that uses native shell functionality – sming Aug 08 '16 at 14:56
  • 1
    I think the OP is trying to avoid using the two commands. This answer is (almost) as valid as doing mkdir foo && cd foo, which isn't handy. – josemigallas Jun 13 '17 at 13:08
  • 21
    The OP is asking for a one-liner that doesn't require to repeat the directory name, and this is it – Jesús Carrera Jun 13 '17 at 18:13
  • This is the traditional one-liner you are referring to or have seen others use in docs/tutorials. There are actually 3 perfect answers here. This one, the one by @jordan-harris , and the selected answer. Depends on your setup and preference. – Wade Feb 14 '19 at 02:14
  • 1
    By the upvotes it's evident many people are finding this answer useful. I'll state why I didn't select this answer: I'm very likely to mistype && cd $_ since the keys are so far away from the home row, so while technically correct, it's not ergonomic. I appreciate the fact that it's not environment dependent and it's useful to know. Thanks! – methodofaction Feb 16 '19 at 22:06
  • This is the best answer! – Roger Dec 18 '19 at 14:10
  • @methodofaction I think people are finding this answer useful and think it should be the accepted answer because in your title you ask for a one-liner, and this is it. It's not called a one-liner when you need to write a separate function with multiple lines in order to do what you want with one line. I understand it's more ergonomic to call the short function name in your accepted answer, but that's not strictly speaking a one-liner. – Jesús Carrera Aug 08 '20 at 09:37
  • @JesúsCarrera if you can point me out a relevant post at Meta or official documentation of this website that suggests/encourages that the accepted answer matches the title (because of seo, for example), I will accept your answer. mkdir newfolder && cd newfolder is also strictly a one-liner. – methodofaction Aug 10 '20 at 15:02
  • @methodofaction yes that's also a one-liner, but later in the content of your question you ask for a way of doing it without repeating the directory name. The official documentation encourages to find What, specifically, is the question asking for?. What you are asking for is a one-liner without repeating the directory name and this is the most accurate answer. – Jesús Carrera Aug 13 '20 at 09:01
  • This is much simpler than the other answer, which provides an answer that (while robust) anyone looking in my .bashrc file would be confused. – mbomb007 Sep 02 '20 at 18:29
  • Any reason you use && instead of ;, as the && version breaks if the directory already exists, while the other works in every situation. – Jonathon May 28 '22 at 22:48
  • weldone @JesúsCarrera – arilwan May 30 '23 at 10:25
217

There's no built-in command, but you can easily write a function that calls mkdir then cd:

mkcd () {
  mkdir "$1"
  cd "$1"
}

Put this code in your ~/.bashrc file (or ~/.kshrc for ksh users, or ~/.zshrc for zsh users). It defines a function called mkcd. "$1" will be replaced by the argument of the function when you run it.

This simple version has several defects:

  • You cannot create a chain of subdirectories at once. Fix: pass the -p option to mkdir. (This may or may not be desirable, as it increases the risk of a typo going undetected, e.g. mkcd mydierctory/newsub will happily create mydierctory and mydierctory/newsub when you meant to create newsub inside the existing mydirectory.)
  • If the argument begins with - but isn't just -, then mkdir and cd will interpret it as an option. If it's just -, then cd will interpret it to mean $OLDPWD. If it's + followed by 0 or more digits, then cd in zsh will interpret it as an index in the directory stack. You can fix the first problem, but not the other two, by passing -- before the argument. You can fix all of these problems by prepending ./ to the argument if it's a relative path.
  • mkdir doesn't follow CDPATH, but cd does, so if you've set CDPATH to a value that doesn't begin with . (an admittedly somewhat unusual configuration), then cd may bring you to a different directory than the one that was just created. Prepending ./ to relative paths fixes this¹ (it causes CDPATH to be ignored).
  • If mkdir fails, it tries to execute cd. Fix: use && to separate the two commands.

Still fairly simple:

mkcd () {
  case "$1" in /*) :;; *) set -- "./$1";; esac
  mkdir -p "$1" && cd "$1"
}

This version still has the potential to make cd go into a different directory from the one that mkdir just created in one edge case: if the argument to mkcd contains .. and goes through a symbolic link. For example, if the current directory is /tmp/here and mylink is a symbolic link to /somewhere/else, then mkdir mylink/../foo creates /somewhere/foo whereas cd mylink/../foo changes into foo (which is /tmp/here/foo). It's not enough to look for symbolic links in the argument, because the shell also tracks symbolic links in its own current directory, so cd /tmp/mylink; mkdir ../foo; cd ../foo does not change into the new directory (/somewhere/else/foo) but into /tmp/foo. A fix for this is to let the cd builtin resolve all .. path components first (it doesn't make sense to use foo/.. if foo doesn't exist, so mkdir never needs to see any ..).

We come to this robust if slightly gory version:

mkcd () {
  case "$1" in
    */..|*/../) cd -- "$1";; # that doesn't make any sense unless the directory already exists
    /*/../*) (cd "${1%/../*}/.." && mkdir -p "./${1##*/../}") && cd -- "$1";;
    /*) mkdir -p "$1" && cd "$1";;
    */../*) (cd "./${1%/../*}/.." && mkdir -p "./${1##*/../}") && cd "./$1";;
    ../*) (cd .. && mkdir -p "${1#.}") && cd "$1";;
    *) mkdir -p "./$1" && cd "./$1";;
  esac
}

(Exercise: why am I using a subshell for the first cd call?)

If mkdir fails, I want to be sure not to change the current directory. Changing back with cd - or $OLDPWD isn't good enough if the shell doesn't have permission to change into its current directory. Also, calling cd updates OLDPWD, so we only want to do it once (or restore OLDPWD).


There are also less specialized ways to not have to retype the word from the previous line:

  • Type cd , then Esc . (or Alt+.) to insert the last argument from the previous command.
  • cd !$ executes cd on the last argument of the previous command.
  • Press Up to recall the previous command line, then edit it to change mkdir into cd.

¹ beware however that it doesn't work in ksh93 since the u+ version, fixed in 93u+m/1.0.0-alpha+d1483150 2021-01-05

  • Thanks! the Esc . seems the most convenient to me, does the key sequence have any special meaning? – methodofaction Mar 12 '11 at 01:36
  • It's just the Bash (and inherited from ksh, and also works in zsh) sequence for "repeat last word of previous command". I use it quite often. – geekosaur Mar 12 '11 at 01:46
  • 38
    @Gilles I'm beginning to think that the "Gilles" account is actually shared by a panel of experts. ;-) – Keith Mar 12 '11 at 03:24
  • @StephaneChazelas /a/b/..// would actually work but not /a/b/../c. Fixed. I've put the question to a broader audience. – Gilles 'SO- stop being evil' Jan 30 '13 at 22:26
  • 1
    mkcd() { mkdir -p "$1" && cd "$1"; } doesn't seem to be a problem in (my instance of) zsh. mkdir -p /var/tmp/somewhere/else /tmp/here; cd /tmp/here; ln -s /var/tmp/somewhere/else mylink; mkdir -p mylink/../foo && cd mylink/../foo; pwd (includes the setup and) displays /tmp/here/foo which is what was created (and what I expected). bash erroneously creates and changes to /var/tmp/somewhere/foo. – Adam Katz Jan 15 '15 at 18:39
  • Why not just use newdir=$(realpath -mL $1) && mkdir -p $newdir && cd $newdir? This should ensure always using a resolved absolute path which shold overcome all edge cases, no? – Dani_l Jan 04 '21 at 23:45
  • @Dani_l But it breaks logical PWD tracking in the shell. – Gilles 'SO- stop being evil' Jan 05 '21 at 09:59
  • @Gilles'SO-stopbeingevil' I've tried the edge cases with symbolic links and .. , and both mkdir and cd worked as intended i.e. mkdir made a directory at /somewhere/foo and cd changed the cwd to at /somewhere/foo . Am i missing something ? – xquilt Feb 18 '22 at 06:45
  • @polendina I give examples in my answer. – Gilles 'SO- stop being evil' Feb 18 '22 at 11:04
  • @Gilles'SO-stopbeingevil' And that's why i'm wondering if i'm missing anything with this very case . The examples state that mkdir mylink/../foo creates /somewhere/else/foo , but for me mkdir creates /somewhere/foo . Also the examples state that cd mylink/../foo changes into foo which is rather ambiguous without the indicating the path , but for me it changes into cd /somewhere/../foo changes the directory to the foo that mkdir created /somewhere/foo . – xquilt Feb 18 '22 at 12:01
  • @polendina /somewhere/else/foo was a mistake, I've fixed it, thanks. It's /somewhere/else/../foo which is /somewhere/foo. cd mylink/../foo is equivalent to cd foo in all the Bourne-style shells I remember seeing unless you disable symbolic directory tracking (e.g. with alias cd='cd -P') – Gilles 'SO- stop being evil' Feb 18 '22 at 16:32
  • @Gilles'SO-stopbeingevil' I indeed don't have cd aliased with this flag . Also, I'm sorry for the befuddling typos that i made at my previous reply . – xquilt Feb 19 '22 at 01:32
37

If you use Oh My Zsh, there's a command called take that does exactly this. It would look something like this.

take myfolder

I actually found this one by accident. I just looked and it's listed on this cheatsheat from the Oh My Zsh GitHub wiki. It's quite a useful command, and apparently very easy to create yourself.

Jordan Harris
  • 471
  • 4
  • 2
  • 2
    I never knew about take :) Perfect! btw - iTerm2 with Oh My Zsh. There are actually 3 perfect answers here. This one, the one by @jesús-carrera , and the selected answer. Depends on your setup and preference. – Wade Feb 14 '19 at 02:12
34

It would never have occurred to me to script up this behaviour because I enter the following on a near-hourly basis ...

$ mkdir someDirectory<ENTER>
$ cd !$

where bash kindly substitutes !$ with the last word of the last line; i.e. the long directory name that you entered.

In addition, filename completion is your friend in such situations. If your new directory was the only file in the folder a quick double TAB would give you the new directory without re-entering it.

Although it's cool that bash allows you to script up such common tasks as the other answers suggest I think it is better to learn the command line editing features that bash has to offer so that when you are working on another machine you are not missing the syntactic sugar that your custom scripts provide.

Rob
  • 441
  • 1
    Is there a good place to find out about most important bash quirks like this one? – dominicbri7 Oct 07 '15 at 02:48
  • @dominicbri7 Generally comes under "bash command line editing" Googling same gives the following as a top result http://web.mit.edu/gnu/doc/html/features_7.html More specifically, !$ is an example of a Word Designator see https://www.gnu.org/software/bash/manual/bashref.html#Word-Designators – Rob Oct 07 '15 at 14:19
  • Just got up-voted lol, TYSM If you liked this, you will also like https://www.gnu.org/software/bash/manual/html_node/Readline-Interaction.html where you can find out about readline which is a library that is integrated in to bash and other command line tools that allows you edit the contents of the line. – Rob Mar 10 '23 at 13:53
18

As per What customizations have you done on your shell profile to increase productivity?, this is how I do it:

# make a directory and cd to it
mcd()
{
    test -d "$1" || mkdir "$1" && cd "$1"
}

it means it also works if the directory already exists.

Mikel
  • 57,299
  • 15
  • 134
  • 153
  • 4
    The -p option to mkdir will suppress errors. – glenn jackman Mar 12 '11 at 05:42
  • 1
    mcd is an already existing command. Though you've just given an example, I used it myself as it's a letter shorter than mkcd. – Dharmit Apr 13 '11 at 14:44
  • @Dharmit Shah: What is the existing mcd command? Which package or project provides this command? – Mikel Apr 13 '11 at 22:05
  • 3
    mtools provides the mcd command. Its man page says "The mcd command is used to change the mtools working directory on the MS-DOS disk." – Dharmit Apr 14 '11 at 06:11
3

Or you could just create a short variable on-the-fly and use it twice x = longproject ; mkdir $x ; cd $x - which I admit is still longer than using a shellscript function :)

sakisk
  • 2,873
1

This is a simple thing to do in a bash script/function. I created a very readable and sufficiently documented tutorial including scripts that works on both Linux and MacOS (this will also be maintained in the future).

My goal for the tutorial complexity is: written for a targeted audience with the only prerequisites being the user has a pulse and can read English, so please provide feedback if you need assistance.

https://github.com/craigopie/shellscripts

mcdir() {
  if [ $# -eq 0 ] || [ $1 = "-h" ] || [ $1 = "--help" ]
    then
      echo "Usage: [option...] {directory}"
      echo "   -p, --path                  Create all directories in path "
      echo "   -h, --help                  Shows this helpful information "
      echo
      return 0
  fi
  ## create directory with a path
  if [ $1 = "-p" ] || [ $1 = "--path" ]
    then
      mkdir -p "$2" &>/dev/null
      if [ $? -gt 0 ]
        then
          echo "There was a problem creating your directory."
          return 1
      fi
      cd "$2" &>/dev/null
      if [ $? -gt 0 ]
        then
          echo "Unable to change into that directory."
          return 1
      fi
      return 0
  fi
  ## create directory in this location
  mkdir "$1" &>/dev/null
  if [ $? -gt 0 ]
    then
      echo "There was a problem creating your directory."
      return 1
  fi
  cd "$1" &>/dev/null
  if [ $? -gt 0 ]
    then
      echo "Unable to change into that directory."
      return 1
  fi
  return 0
}
export -f mcdir

To install this you have two options:

  • First is add the function to your .bashrc file.
  • The second is to add the file to your path and then source the file in your .bash_profile file.

Again, the README file is very detailed on how to do this.

Good luck!

AdminBee
  • 22,803
Craig Opie
  • 11
  • 1
1

If you are using Oh-my-zsh, take command will do the trick. Take a look at the cheat sheet.

0

Here's a slight variant which is worthy of mention:

function mkdir() {
    local dir=$1
    command mkdir "$dir"  &&  cd "$dir"
}

Add this to your ~/.bash_profile and you can then use mkdir as normal (once you've source'd it), except now it will run the function above rather than the standard mkdir command.

Note, this does not validate input as per the accepted answer by Gilles, but demonstrates how you can (effectively) override builtins.

From the docs (paraphrasing slightly):

command mkdir [args] runs mkdir with args ignoring any shell function named mkdir. Only shell builtin commands or commands found by searching the PATH are executed. If there is a shell function named ls, running command ls within the function will execute the external command ls instead of calling the function recursively

I believe builtin achieves a similar result to command.

Arj
  • 101
0

Adding helper function to BASH, ZSH or KSH

Create mkcd command to your environment in one line

echo -e 'mkcd() {\n mkdir -p "$1" && cd $_\n}' >> ~/.${0//-/}rc && . ~/.${0//-/}rc
Proximo
  • 165
0

I know the user is in Bash, but I use both Bash and Fish. For Fish shell users, you can use this function (source), just put it into a file called ~/.config/fish/functions/mkcd.fish and restart your terminal, then use mkcd FOLDERNAME

function mkcd -d "Create a directory and set CWD"
    command mkdir $argv
    if test $status = 0
        switch $argv[(count $argv)]
            case '-*'
        case '*'
            cd $argv[(count $argv)]
            return
    end
end

end

Vijay Prema
  • 171
  • 1
  • 4
-1

Just automated the above answers and made a one time executable script:

fun='
mkcd ()
{
    mkdir -p -- "$1" && cd -P -- "$1"
}'

echo "$fun" >> ~/.bashrc

Just copy this in a new file mkcd.sh and run it only once in terminal by bash mkcd.sh. Then execute source ~/.bashrc to make it working in the current session.

After this, you can use mkcd some_dir to create and enter directly in that directory.

  • 1
    You suggest writing a script (in a file) whose sole purpose is to append to the ~/.bashrc file (with an answer that has already been given)?  And how do you suggest creating this mkcd.sh script?  With an editor, perhaps?  This looks like more work than just editing ~/.bashrc.  What advantage does this have over the accepted answer?  … … … … … … … … … … … … … … … … … … … … … … … … … … …  P.S. This will fail because of quoting issues, which tells me that you haven’t even tried it yourself. – Scott - Слава Україні Dec 11 '17 at 20:52
  • I am sorry to say but yes as I wrote in my answer I used the above answers. It does work. If you don't believe me, try it. And about not trying, I used my complete day to write such scripts in bash and python today. – subtleseeker Dec 11 '17 at 21:11
  • I did try what you wrote.  It does not work. – Scott - Слава Україні Dec 11 '17 at 21:21
-1

Good answers already posted.

You could also create an alias in .bashrc by adding

alias newdir mkdir %1;cd %1

at the bottom of .bashrc. After saving .bashrc and closing the terminal and opening a new terminal typing

newdir directoryname

Will execute

mkdir directoryname;cd directoryname

Note that if directoryname already exists this may display an error then cd to the directory. If you attempt to do newdir in a directory you don't have permission in you will get 2 errors if the directory doesn't already exist.

The advantage to this solution is that you don't have to remember to do ;cd $_ to create a new directory and change to it. Also, this "new" command will be persistent between reboots of the system, and opening new terminal instances.

  • 1
    I don't know which shell you tested this in, but it definitely doesn't work in bash. Aliases in bash don't support arguments in this way. Is this fish or some other shell? – muru Oct 17 '23 at 23:57