Is there a one-liner that allows me to create a directory and move into it at the same time?
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 tomkdir
. (This may or may not be desirable, as it increases the risk of a typo going undetected, e.g.mkcd mydierctory/newsub
will happily createmydierctory
andmydierctory/newsub
when you meant to createnewsub
inside the existingmydirectory
.) - If the argument begins with
-
but isn't just-
, thenmkdir
andcd
will interpret it as an option. If it's just-
, thencd
will interpret it to mean$OLDPWD
. If it's+
followed by 0 or more digits, thencd
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 followCDPATH
, butcd
does, so if you've setCDPATH
to a value that doesn't begin with.
(an admittedly somewhat unusual configuration), thencd
may bring you to a different directory than the one that was just created. Prepending./
to relative paths fixes this¹ (it causesCDPATH
to be ignored).- If
mkdir
fails, it tries to executecd
. 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/else/foo
whereas cd mylink/../foo
changes into 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 !$
executescd
on the last argument of the previous command.- Press Up to recall the previous command line, then edit it to change
mkdir
intocd
.
¹ 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
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 )
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.