mkdir -p for files

14,803

Solution 1

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

Solution 2

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"

Solution 3

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'

Solution 4

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.

Solution 5

It's possible to "fake it".

First, some required theory:

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!'

Combining the Concepts:

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 touches the filename in it's respective directory.

Here's a breakdown of what happens in the above command:

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
  • because we used -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
  • we then run a shell command that uses the && 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
  • we now have a variable we can pass to mkdir -p, but we need to cut out the filename. Simple enough using '${f%/*}':
    • mkdir -p deeply/nested/foo/
    • mkdir -p deeply/nested/baz/
  • and then we just re-reference the f variable in its entirety when we touch:
    • touch deeply/nested/foo/bar.txt
    • touch deeply/nested/baz/fiz.txt
Share:
14,803

Related videos on Youtube

user1730706
Author by

user1730706

Hello world

Updated on September 18, 2022

Comments

  • user1730706
    user1730706 almost 2 years

    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?

    • Admin
      Admin over 11 years
      Not that I am aware of.. but you could just do mkdir -p /path/to/make && touch /path/to/file... Which would make an empty file in that new directory structure you created all as needed.
    • Admin
      Admin over 11 years
      @Kansha combine that with dirname and basename and we'll only need the single argument; profit! :)
    • Admin
      Admin over 11 years
      Aye, good call.
  • derobert
    derobert over 11 years
    The test isn't needed; mkdir -p doesn't do anything if the dir already exists. Doesn't even return an error.
  • user
    user over 11 years
    And of course, this only creates the directory.
  • user1730706
    user1730706 over 8 years
    You have all this explaination for what is essentially 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
  • razorbeard
    razorbeard over 8 years
    I'll admit I'm not the most clued up on *nix commands - I don't even know what UUOC is - but I fail to see how this is an awful answer. It is researched, tested and is a working solution to your original question. It's constructive (unlike your rather rude comment). Please, if you feel that I've done something irresponsible here that I should not have, then elaborate further - explain why. I don't care much for opinionated debate that has no substance.
  • user1730706
    user1730706 over 8 years
    My last comment explains it perfectly. Your entire answer could be better written as while read f; do mkdir -p "$(dirname "$f")"; touch "$f"; done < files.txt. The fact that you cant do a 10 second internet search for UUOC is also telling
  • ecoologic
    ecoologic about 5 years
    My man! - thank you, I called it touchp, like mkdir -p