mkdir -p
will create a directory; it will also make parent directories as needed.
Does a similar command exist for files, that will create a file and parent directories as needed?
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems. It only takes a minute to sign up.
Sign up to join this communityThe install
utility will do this, if given the source file /dev/null
. The -D
argument says to create all the parent directories:
anthony@Zia:~$ install -D /dev/null /tmp/a/b/c
anthony@Zia:~$ ls -l /tmp/a/b/c
-rwxr-xr-x 1 anthony anthony 0 Jan 30 10:31 /tmp/a/b/c
Not sure if that's a bug or not—its behavior with device files isn't mentioned in the manpage. You could also just give it a blank file (newly created with mktemp
, for example) as the source.
No, it does not as far as I know. But you can always use mkdir -p
and touch
after each other:
f="/a/b/c.txt"
mkdir -p -- "${f%/*}" && touch -- "$f"
I frequently ran into this kind of situation, so I simply wrote a function in my .bashrc
file. It looks like this
function create() {
arg=$1
num_of_dirs=$(grep -o "/" <<< $arg | wc -l)
make_dirs=$(echo $arg | cut -d / -f1-$num_of_dirs)
mkdir -p $make_dirs && touch $arg
}
So, when I want to create a file inside a path of non-existent directories, I will say
create what/is/it # will create dirs 'what' and 'is', with file 'it' inside 'is'
mkdir -p parent/child && touch "$_/file.txt"
$_
is a shell parameter, it expands to last argument of previous command. Example mkdir test && cd "$_"
will create and cd
into the directory test
.
Add the following to your ~/.bashrc
:
function mktouch() {
mkdir -p -- "$(dirname -- "$1")" && touch -- "$1"
}
Then reload bash
then use it like mktouch your/path/file.txt
.
It's possible to "fake it".
Rob Griffiths posted an article in 2007 entitled Easily Create Lots of New Folders on Macworld.com wherein he discussed using the xargs
command to read in a list of files to create directories using mkdir
.
xargs
is capable of referencing a placeholder
({}
) with the -I
flag,
which contains the value for each argument passed to xargs
. Here's the difference between with that flag, and without:
$ foo.txt bar.txt | xargs echo
$ => foo.txt bar.txt
$ foo.txt bar.txt | xargs -I {} echo {}
$ => foo.txt
$ => bar.txt
xargs
is also capable of running arbitrary shell commands with the sh -c
flag:
foo.txt bar.txt | xargs sh -c 'echo arbitrary command!'
We can combine these concepts with mkdir -p
instead of mkdir
and the concept in @ldx's answer to produce this:
$ cat files.txt | xargs -I {} sh -c 'f="{}" && mkdir -p -- "${f%/*}" && touch -- "$f"'
This command basically maps each filename in a line-separated list of files, chops off the file part, creates the directories with mkdir -p
and then touch
es the filename in it's respective directory.
Say for instance my files.txt
looks like this:
deeply/nested/foo/bar.txt
deeply/nested/baz/fiz.txt
cat files.txt
produces deeply/nested/foo/bar.js
deeply/nested/baz/fiz.txt
deeply/nested/foo/bar.js
deeply/nested/baz/fiz.txt
is piped to xargs
-I {}
, xargs
will translate each argument to it's own command, so we now have:
deeply/nested/foo/bar.txt
deeply/nested/baz/fiz.txt
&&
combinator to group 3 commands that run sequentially - the first command stores the file in an environment variable (that gets re-used on the next file pass) using the placeholder we registered before, so we now have:
f=deeply/nested/foo/bar.txt
f=deeply/nested/baz/fiz.txt
mkdir -p
, but we need to cut out the filename. Simple enough using '${f%/*}'
:
mkdir -p deeply/nested/foo/
mkdir -p deeply/nested/baz/
f
variable in its entirety when we touch
:
touch deeply/nested/foo/bar.txt
touch deeply/nested/baz/fiz.txt
cat files.txt | xargs -I {} sh -c 'f="{}" && mkdir -p -- "${f%/*}" && touch -- "$f"'
, which has UUOC, then you subshell into xargs which subshells back into the shell, when a while read
loop makes more sense
I was going to suggest as it keeps it on one line, though setting the variable separately allows you to change it and rerun the command from the history pretty easily.
B="./make/this/path" && mkdir -p -- "$B" && touch -- "$B/file.txt"
$ mkdir -p foo/bar/baz.c && rmdir $_ && touch $_
This solution:
mkdir
creates the full directory path, rmdir
removes the last portion, and touch
recreates the last portion as a file. The $_
pseudo-argument just inserts the last argument of the previous command.
As a bonus, this method completes Alt-.
with $_
, which is the newly-completed file name. Thus, a simple vim Alt-.
will open the new file.
Note that filenames with spaces or other non-A-Za-zא-ת0-9._-
characters should quote the argument shortcuts:
$ mkdir -p foo/bar/baz.c && rmdir "$_" && touch "$_"
I personally don't take this much care, but you should be aware of the limitation. Thank you to StéphaneChazelas for mentioning it in the comments.
(){mkdir -p $1:h && touch $1} path/to/file
Jul 11, 2022 at 11:28
$_
to prevent split+glob. You can omit them if you know the values of $_
doesn't contain wildcard characters or characters of $IFS
(which involves knowing the current value of $IFS
). Which is why I said it was zsh syntax as zsh doesn't do split+glob upon parameter expansion.
Jul 11, 2022 at 12:19
dir=$(dirname "$f")
test -d $dir || mkdir -p "$dir"
test
isn't needed; mkdir -p
doesn't do anything if the dir already exists. Doesn't even return an error.
dirname
andbasename
and we'll only need the single argument; profit! :)