Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems. It's 100% free, no registration required.

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

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?

share|improve this question

migrated from stackoverflow.com Jan 30 '13 at 15:05

This question came from our site for professional and enthusiast programmers.

    
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. – Kansha Jan 30 '13 at 9:10
1  
@Kansha combine that with dirname and basename and we'll only need the single argument; profit! :) – akaIDIOT Jan 30 '13 at 9:12
    
Aye, good call. – Kansha Jan 30 '13 at 9:14
up vote 12 down vote accepted

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

share|improve this answer

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"
share|improve this answer
dir=$(dirname "$f")
test -d $dir || mkdir -p "$dir"
share|improve this answer
2  
The test isn't needed; mkdir -p doesn't do anything if the dir already exists. Doesn't even return an error. – derobert Jan 30 '13 at 15:36
    
And of course, this only creates the directory. – Michael Kjörling Jan 30 '13 at 15:40

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"
share|improve this answer

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|improve this answer
1  
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 – Steven Penny Mar 10 at 20:53
    
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. – razorbeard Mar 10 at 21:03
    
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 – Steven Penny Mar 10 at 22:04

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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