20

I would like to create many directories using mkdir. Each directory name will consist of a prefix (a string) and an index (an integer). Suppose that I would like the prefix to be "s" and the indices to range from 1 to 50. This means that I would like to create directories titled:

s1, s2, ... , s49, s50

Is there a way to do this automatically using mkdir? Thanks for your time.

3
  • 2
    Which shell are you using?
    – user22304
    Sep 21, 2012 at 12:54
  • @FrancescoTurco I'm using bash. Thank you for your time!
    – Andrew
    Sep 21, 2012 at 14:04
  • 3
    FYI, I find it much more useful to use fixed-width indices, like: s01, s02, ... , s49, s50. If I used fixed-width indices, /bin/ls almost always produces the order I want.
    – Rob
    Sep 21, 2012 at 20:48

5 Answers 5

45
  • One

    for i in {1..50}; do
      mkdir s"$i"
    done
    
  • Two

    mkdir s{1..50}
    

    This option works in bash, zsh and ksh93

  • Three

    mkdir $(printf "s%02i " $(seq 1 50))
    
9
  • 4
    Why would you ever use one over two?
    – Kevin
    Sep 21, 2012 at 1:46
  • 9
    you may want to do more with $i than just make a directory e.g mkdir s$i ; echo $i > s$i/$i. Also, One is a nice, simple example of using a for loop in bash...on a site like this it's not at all unlikely for a novice user to see it and think "nice, i didn't realise you could do that" ---> enlightenment.
    – cas
    Sep 21, 2012 at 2:47
  • 1
    @rahmu It's a feature found in Bash/Zsh (probably ksh93 too). No need to downvote. Sep 21, 2012 at 12:58
  • 4
    @rahmu: I think one should only consider if jhonathan's answer fixes the problem with the OP's shell (bash), not necessarily with others.
    – user22304
    Sep 21, 2012 at 14:20
  • 4
    @Kevin while it's not likely to be an issue for fifty, if you've got five hundred directories to make you might worry about argument limits.
    – Random832
    Sep 21, 2012 at 14:49
34

You can do this with a shell script.

Pure sh - this will work even on pre-POSIX bourne shells:

n=1;
max=50;
while [ "$n" -le "$max" ]; do
  mkdir "s$n"
  n=`expr "$n" + 1`;
done

If you want to create a high number of directories, you can make the script faster by reducing it to a single call of mkdir as well as using shell builtins for testing and arithmetics. Like this:

n=1
max=50
set -- # this sets $@ [the argv array] to an empty list.

while [ "$n" -le "$max" ]; do
    set -- "$@" "s$n" # this adds s$n to the end of $@
    n=$(( $n + 1 ));
done 

mkdir "$@"

Zsh, ksh93 or bash make this much easier, but I should point out this is not built into mkdir and may not work in other shells. For larger cases, it may also be affected by limits on the number or total size of arguments that may be passed to a command.

mkdir s{1..50}
5
  • 3
    +1 This is great! I gotta nitpick about one thing, though: n=$(( n + 1 )) would've been just as strictly POSIX and wouldn't cost you a subshell.
    – kojiro
    Sep 21, 2012 at 5:04
  • @rahmu the [[ keyword is not POSIX. (Not to mention that [ is a shell builtin in many modern shells.)
    – kojiro
    Sep 21, 2012 at 12:03
  • @kojiro: [[ works on my ksh88 (which predates bash) so I assumed it was POSIX. I couldn't find any mention of it in the POSIX spec so I believe you're right. Thanks for the info!
    – rahmu
    Sep 21, 2012 at 12:23
  • I thought about adding a version using arithmetic expansion, but wanted to limit "pure sh" to stuff that would have worked on pre-POSIX versions as well. I also didn't mention seq for the same reason - if you have seq you probably have bash. The loop example is also good for bypassing argument limits for a larger number of directories, which also means there's a place for for i in {range} for users of advanced shells.
    – Random832
    Sep 21, 2012 at 14:50
  • The zsh hint at the end is amazing, it saved me so much time!
    – Jase
    Dec 18, 2018 at 12:44
12

Lots of complicated answers here, but bash makes it really easy. Sure, the pure POSIX solution works, but why not take advantage of the bash shell you're using, anyhow? You can do this easily with brace expansion:

% mkdir -v s{1..10} && ls -1d s{1..10}                                   (09-24 17:37)
mkdir: created directory `s1'
mkdir: created directory `s2'
mkdir: created directory `s3'
mkdir: created directory `s4'
mkdir: created directory `s5'
mkdir: created directory `s6'
mkdir: created directory `s7'
mkdir: created directory `s8'
mkdir: created directory `s9'
mkdir: created directory `s10'
s1
s10
s2
s3
s4
s5
s6
s7
s8
s9
7

mkdir $(seq --format 's%.0f' 1 50)

or if you want zero-padded numbers (which would be better for sorting):

mkdir $(seq --format 's%02.0f' 1 50)

or:

mkdir s$(seq -s ' s' -w 1 50) -- note the string 's' just before the $(), without it the first directory created will be just '01' rather than 's01'

and, finally: mkdir $(printf "s%02i " $(seq 1 50))

seq is from GNU Coreutils

oddly, seq's --format or -f option only allows printf's floating point double types (like f and g. also a strange floating point hex format that i've never found any use for). I have no idea why. It would be nice if it also supported other printf(3) numeric types like integer (d,i), octal (o,U) or hex (x,X).

Anyway, a double format with 0 decimal precision like %.0f or %02.0f is close enough to an integer for this purpose.

$ seq --help
Usage: seq [OPTION]... LAST
  or:  seq [OPTION]... FIRST LAST
  or:  seq [OPTION]... FIRST INCREMENT LAST
Print numbers from FIRST to LAST, in steps of INCREMENT.

  -f, --format=FORMAT      use printf style floating-point FORMAT
  -s, --separator=STRING   use STRING to separate numbers (default: \n)
  -w, --equal-width        equalize width by padding with leading zeroes
      --help     display this help and exit
      --version  output version information and exit

If FIRST or INCREMENT is omitted, it defaults to 1.  That is, an
omitted INCREMENT defaults to 1 even when LAST is smaller than FIRST.
FIRST, INCREMENT, and LAST are interpreted as floating point values.
INCREMENT is usually positive if FIRST is smaller than LAST, and
INCREMENT is usually negative if FIRST is greater than LAST.
FORMAT must be suitable for printing one argument of type `double';
it defaults to %.PRECf if FIRST, INCREMENT, and LAST are all fixed point
decimal numbers with maximum precision PREC, and to %g otherwise.

See also: http://www.gnu.org/software/coreutils/manual/html_node/seq-invocation.html

2
  • {1,50} or {01,50} (for zero-padding) is much easier and more understandable.
    – Kevin
    Sep 21, 2012 at 1:48
  • 1
    true...if you're using bash rather than sh. that's why i upvoted @Random832's answer. it's a good answer. using seq is also a useful answer.
    – cas
    Sep 21, 2012 at 2:27
4

Just to be different, here's a POSIX sh solution that uses recursion:

makedirs() {
  [ "$1" -gt 0 ] || return
  mkdir "s$1"
  makedirs $(( $1 - 1 ))
}
$ makedirs 9
$ ls
s1  s2  s3  s4  s5  s6  s7  s8  s9

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .