6

I know I can create a series of nested directories via a rather verbose series of mkdir -p with the -p flag applied to suppress warnings that the directory already exists. This gets very verbose with individual calls, i.e.:

mkdir -p hello
mkdir -p hello/i_am_a
mkdir -p hello/i_am_a/boy
mkdir -p hello/i_am_a/girl
mkdir -p hello/i_am_a/alien
mkdir -p hello/i_am_a/dog
mkdir -p hello/my_name_is
mkdir -p hello/my_name_is/jim
mkdir -p hello/my_name_is/bob
mkdir -p hello/my_name_is/secret

I would like to instead build the tree or at least part of it in a single call with a nested pattern consisting of a list-opening character (i.e. {), list delimiter character (i.e. ,)., and list-closing character (i.e. }).

For each level starting with the top, a directory of that name is created if it does not exist already.

To continue my silly example above with real words, I would use the input string:

'hello{i_am_a{boy,girl,alien,dog},my_name_is{jim,bob,secret}}'

...which should create the following...

hello
hello/i_am_a
hello/i_am_a/boy
hello/i_am_a/girl
hello/i_am_a/alien
hello/i_am_a/dog
hello/my_name_is
hello/my_name_is/jim
hello/my_name_is/bob
hello/my_name_is/secret

For convenience, let's say that if it impacts the solution's length, we can assume there's no more than n layers of nesting in the resultant tree (e.g. n=3 in my example above, for instance).

Let us further assume for convenience that the three special structure characters (in the above example, for instance {, ,, and }) will never occur in the nested strings, i.e. they will never show up in the resulting file paths.

It seems a Perl or Python embedded script might do the trick, but perhaps there's a simpler solution using built-in BASH functionality?

My goal is a concise solution that cuts down on the verbosity of my data post-processing scripts, which often involve the creation of hierarchical directory trees populated at deeper levels with intermediates that I save for reuse and with finished results in the directories towards the top of the tree.

Jason R. Mick
  • 5,177
  • 4
  • 40
  • 69
  • 9
    You've been here long enough that you should know this type of "write my code for me" request is completely off-topic for StackOverflow. Please review [ask] and visit the [help]. – Jim Garrison Mar 31 '16 at 06:09
  • There's a number of highly rated questions asking for very specific one-liners. In this case I'm simply asking how to implement a small very specific action with the method being ambiguous. This is really no different from asking "how do I get this regular expression to work". I don't think soliciting concise/well made one-liners constitutes "write my code" if done sparingly... – Jason R. Mick Mar 31 '16 at 06:13
  • 1
    Look at F. Hauri's solution! For all your condescension there was a simple answer to accomplish this! BTW, this was a similar but slightly different question and the community clearly found it acceptable: http://stackoverflow.com/questions/2445213/building-a-directory-tree-from-a-list-of-file-paths Just because YOU don't have the specific solution for a specific query doesn't mean you should attack those who post it if the objective is limited and clearly defined. Again if we were to apply your standard perhaps 50% of the scripting related questions rated 2 or higher would go away... – Jason R. Mick Mar 31 '16 at 06:16
  • @JimGarrison: As the answer could be a lot shorter than question, I think question reflect some effort and some misunderstanding... It's the reason because ***what have you already tried*** could not be an answer, in this case! (I think) – F. Hauri - Give Up GitHub Mar 31 '16 at 06:29
  • 3
    Meta discussion about this question: http://meta.stackoverflow.com/questions/320121/is-this-write-me-a-bash-one-liner-type-question-on-topic – user000001 Mar 31 '16 at 06:43
  • FYI my previous solution consisted of `mkdir -p calls ordered via nesting, which might be somewhat obvious via my example output. I don't think putting that really changes the validity of the question. Also @JimGarrison your recent question http://stackoverflow.com/questions/1574898/bash-and-filenames-with-spaces is rather open ended and could easily fall under the same criticism you voiced. Irony? – Jason R. Mick Mar 31 '16 at 06:45
  • 2
    @JasonR.Mick - That question was asked 7 years ago where rules/guidelines were slightly different, however it still does show what Jim Garrison had tried/researched. Even if it is "trivial", it is still a good idea to show what you have tried, it helps a lot to explain what kind of solution you were looking for and you don't end up with answers that tell you to "try this". I invite you to discuss this on the meta discussion instead of this question if you wish – Sayse Mar 31 '16 at 06:57
  • 2
    @Sayse Good point. Perhaps he could have reminded me of that first though and then moved to close if I refused to improve the question. Personally I think that would have been more productive. Either way, I think I've edited the question per your suggestion (which he also implied I think) and would like it to be reopened if possible given that now it's more explicit – Jason R. Mick Mar 31 '16 at 07:46
  • 1
    Maybe so, imo your edit has made a big difference and your question currently has 2 reopen votes now. – Sayse Mar 31 '16 at 07:56
  • Boo, hiss re: specifying `{foo,bar,...}` syntax specifically *as an element of the question*. Granted, that's the right tool for the immediate job here, but if there were an even better tool, writing your question in such a way as to categorically exclude it is poor form. – Charles Duffy Apr 01 '16 at 17:34

1 Answers1

19

Try this:

mkdir -p hello/{i_am_a/{boy,girl,alien,dog},my_name_is/{jim,bob,secret}}

seems to do the job:

find *
hello
hello/i_am_a
hello/i_am_a/dog
hello/i_am_a/alien
hello/i_am_a/boy
hello/i_am_a/girl
hello/my_name_is
hello/my_name_is/bob
hello/my_name_is/secret
hello/my_name_is/jim

... further?

This will create 30 files (named file_0a to file_2j) in each of this directories:

touch hello/{i_am_a/{boy,girl,alien,dog},my_name_is/{jim,bob,secret}}/file_{0..2}{a..j}

( For populating this 210 files, you could replace touch by date | tee , for sample. )

find * -ls | sed -ne '1,7{p;d};:;N;$!b;${s/^.*\(\(\n[^\n]*\)\{5\}\)\n*$/...\1/p;}'
1703938   4 drwxr-xr-x   4 user user  4096 avr  3 22:23 hello
1703939   4 drwxr-xr-x   6 user user  4096 avr  3 22:23 hello/i_am_a
1703943   4 drwxr-xr-x   2 user user  4096 avr  3 22:23 hello/i_am_a/dog
1704066   0 -rw-r--r--   1 user user     0 avr  3 22:23 hello/i_am_a/dog/file_2i
1704057   0 -rw-r--r--   1 user user     0 avr  3 22:23 hello/i_am_a/dog/file_1j
1704061   0 -rw-r--r--   1 user user     0 avr  3 22:23 hello/i_am_a/dog/file_2d
1704052   0 -rw-r--r--   1 user user     0 avr  3 22:23 hello/i_am_a/dog/file_1e
...
1704076   0 -rw-r--r--   1 user user     0 avr  3 22:23 hello/my_name_is/jim/file_0i
1704097   0 -rw-r--r--   1 user user     0 avr  3 22:23 hello/my_name_is/jim/file_2j
1704074   0 -rw-r--r--   1 user user     0 avr  3 22:23 hello/my_name_is/jim/file_0g
1704073   0 -rw-r--r--   1 user user     0 avr  3 22:23 hello/my_name_is/jim/file_0f
1704081   0 -rw-r--r--   1 user user     0 avr  3 22:23 hello/my_name_is/jim/file_1d

... And as Jonathan Leffler suggest, have a look at man -Pless\ +/Brace.Expansion bash

eval and printf for different content into many different files

Instead of touch you could: eval <(printf 'echo $RANDOM >%s\n' ...):

mkdir -p hello/{i_am_a/{boy,girl,alien,dog},my_name_is/{jim,bob,secret}}
eval <(printf 'echo "$RANDOM" >%s\n' \
  hello/{i_am_a/{boy,girl,alien,dog},my_name_is/{jim,bob,secret}}/file_{0..2}{a..j}
)
F. Hauri - Give Up GitHub
  • 64,122
  • 17
  • 116
  • 137