Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating Hierarchical Directory Tree From Compact String Representation

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.

like image 926
Jason R. Mick Avatar asked Mar 31 '16 06:03

Jason R. Mick


1 Answers

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

like image 118
F. Hauri Avatar answered Sep 25 '22 01:09

F. Hauri