Take the 2-minute tour ×
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.

I want to create a directory in such a way that I need to label the directories from a to z. Inside each of these directories, I need to create sub-directories so that they are labelled as aa, ab etc.

So, for instance, for the directory m, my sub-directories will be labelled as ma, mb upto mz.

share|improve this question
3  
you don't need the first mkdir because -p is creating the subdirectories too. And you don't need the cd command if you use abolute paths. Otherwise it's nice, working, variables declared, etc. Michael Homer's version is shorter and maybe has better performance, but for such a simple task yours would be good enough. –  user78262 Jul 22 at 0:17
1  
Back in the day there were more termininfo entries and no bash {a..z} syntax so I'd list out /usr/share/terminfo with [a-z] and use that output. Linux is pretty sparse on that count, used to be entries for all upper and lower case characters plus the digits. –  schemathings Jul 22 at 3:53

4 Answers 4

up vote 19 down vote accepted

Try:

for x in {a..z} ; do mkdir -p $x/${x}{a..z} ; done

Bash will expand XXX{a..z} out to XXXa, XXXb, and so on. There's no need for the inner loop you have.

After that:

$ ls
a  b  c  d  e  f  g  h  i  j  k  l  m  n  o  p  q  r  s  t  u  v  w  x  y  z
$ ls m
ma  mc  me  mg  mi  mk  mm  mo  mq  ms  mu  mw  my
mb  md  mf  mh  mj  ml  mn  mp  mr  mt  mv  mx  mz
share|improve this answer

So, with bash's alphabet expansion thing, this works:

set {a..z}
for a do printf "./$a/$a%s\n" "$@"
done | xargs mkdir -p

And if you just type out the alphabet once in the first line the same concept should be portable to any shell. There are other ways to arrive at the set line if you don't want to type it like:

seq -sP32P 97 123|dc
a b c d e f g h i j k l m n o p q r s t u v w x y z 

...for instance works in an ASCII locale. So you could do set $(seq -sP32P 97 123|dc) or any other command that would get you an $IFS separated list of the arguments you need, but, I mean, it's probably better just to use the bash thing or to type it.

Anyway, I think that's the way I would do it if only because it only invokes mkdir as often as necessary.

And just to demonstrate how it works, here's a little debug output of a smaller set:

sh -cx 'for n do printf "./$n/$n%s\n" "$@"; done|cat' -- arg1 arg2 arg3
+ for n in '"$@"'
+ printf './arg1/arg1%s\n' arg1 arg2 arg3
+ cat
+ for n in '"$@"'
+ printf './arg2/arg2%s\n' arg1 arg2 arg3
+ for n in '"$@"'
+ printf './arg3/arg3%s\n' arg1 arg2 arg3
./arg1/arg1arg1
./arg1/arg1arg2
./arg1/arg1arg3
./arg2/arg2arg1
./arg2/arg2arg2
./arg2/arg2arg3
./arg3/arg3arg1
./arg3/arg3arg2
./arg3/arg3arg3

As you can see, the for only loops once per positional parameter array index, which I here set by simply handing sh the parameters at invocation, and above with set ${positionals}. But printf receives the same array in its argument list for each iteration and applies its format string to each of its arguments, so you get the semblance of recursion without any unnecessary recursion.

And adding the done|command will stream all of a for loop's output over the pipe in the same way done >file would stream it all into a file - only opening and closing the output file once for the entire for...done construct.

share|improve this answer

From here, I ended up creating a script like this. However, if I get an elegant solution, I will accept it as an answer.

for x in {a..z}
do
    mkdir -p /home/ramesh/$x
    cd /home/ramesh/$x
    for y in {a..z}
    do
        mkdir -p /home/ramesh/$x/$x$y
    done
done
share|improve this answer
    
+1, good question and better answer :) –  Networker Jul 21 at 23:37
    
@Networker, I am sure there is a pretty good solution than this one. Waiting for some expert answer which will do the job as similar as this answer, but will look lot cooler :) –  Ramesh Jul 21 at 23:38
    
Strictly speaking, it’s not necessary when you know what the variable values are going to be (and they are simple strings), but it’s a good idea to get into the habit of quoting your variables. You could use /home/ramesh/"$x"/"$x$y", /home/ramesh/"$x/$x$y", or "/home/ramesh/$x/$x$y" – they’re all equivalent. –  Scott Jul 22 at 16:12

In Perl:

perl -MFile::Path=make_path -e '
    make_path(map { $l=$_; map { "$l/$l$_" } a..z } a..z)
'

File::Path is core module since Perl 5.001.

share|improve this answer

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.