40

Suppose I have a directory structure like this:

$ [~/practice] ls
a/ b/ c/ d/

Now I want to create a directory tmp1 in all sub directories of practice and I do this:

$ [~/practice] mkdir */tmp1
mkdir: cannot create directory `*/tmp1': No such file or directory

Then I try the -p switch and I endup with a directory named * with a sub directory tmp1

$ [~/practice] mkdir -p */tmp1

$ [~/practice] ls
*/ a/ b/ c/ d/

I know the use of -p switch is to create multiple nonexistent directories. I just thought it might help.

How do I create tmp1 in all subdirectories at once?

If this can be done, how do I extend it to create \tmp1, \tmp2, \tmp3 in \a, \b and \c at once?

Edit: I missed mentioning that the directories don't have to be simple and in order, like a, b, c etc., and the directory to be created is not necessarily like tmp1, tmp2.

$ [~/practice] ls
dog/ cat/ rat/

In them, I would like to have something like

$ [~/practice] ls *
dog:
red/ blue/

cat:
red/ blue/

rat:
red/ blue/
3
  • 1
    Given Christopher's answer, you should clarify whether "create \tmp1, \tmp2, \tmp3 in \a, \b and \c" should result a total of 3 or 9 subdirectories.
    – manatwork
    Jan 20, 2013 at 15:24
  • @manatwork: That's a good point. It should create 9 subdirectories.
    – Animesh
    Jan 20, 2013 at 15:27
  • This my help to solve the space issue in the name of the directory sudo find ./ -depth -type d -exec echo '"{}/RCS"' \;
    – user114674
    May 8, 2015 at 15:16

10 Answers 10

54

With globs :

for dir in */; do mkdir -- "$dir/tmp1"; done

NOTE

  • I treat only dirs (including symlinks to dirs) with the little hack of using */ as a glob
  • If you want to create multiple subdirs at once :

    for dir in */; do mkdir -- "$dir"/{tmp1,foo,bar,qux}; done

11
  • Could I do for i in */; do mkdir "$i/itemone" "$i/itemtwo"; done for multiple, not-in-order directory names?
    – Animesh
    Jan 20, 2013 at 15:36
  • I tested this and it is very much possible. Thanks.
    – Animesh
    Jan 20, 2013 at 15:39
  • See my edited post Jan 20, 2013 at 15:42
  • 1
    Brace expansion is not performed in quoted string.
    – manatwork
    Jan 20, 2013 at 16:13
  • Yes, thanks, post edited by Stephane Chazelas accordingly... Jan 20, 2013 at 22:11
11

[...] how do I extend it to create \tmp1, \tmp2, \tmp3 in \a, \b and \c at once?

 mkdir {a,b,c}/tmp{1,2,3}
10

With GNU or BSD* find:

find -mindepth 1 -maxdepth 1 -type d -exec mkdir {}/newdir \;

or using parameter expansion:

dirs=(*/)
mkdir -- "${dirs[@]/%/newdir}"

*Includes OS X

2
  • 2
    With standard find syntax: find . -name . -o -prune -type d -exec sh -c 'exec mkdir "$1/newdir"' sh {} \; Jan 20, 2013 at 22:04
  • 1
    Also note the differences between the two solutions: the first one includes hidden directories, the second one will add a newdir in the directories linked by any symlink in the current directory. Jan 20, 2013 at 22:06
6

Off topic since you're mentioning bash, but for the record, with zsh, you'd do:

dirs=(*(/))
mkdir -- $^dirs/tmp1

$^var turns on brace-like expansion for the expansion of the array. It's reminiscent of rc's ^ operator and in rc (or its derivative es), you'd write it:

dirs=(*/)
mkdir -- $dirs^tmp1

However note (and the same applies to the bash solutions given here) that in the rc solution dirs would also contain symbolic links to directories. In the zsh solution, change *(/) to *(-/) if you want to include symlinks to directories.

1
  • +1 for your point about symlinks! This behavior may be desired, but mkdir's complaint about creating a duplicate directory can be suppressed with the -p flag.
    – kojiro
    Jan 20, 2013 at 21:46
4

Portably, loop over the parent directories:

for d in */; do mkdir "$d/red" "$d/blue"; done

Add -- after mkdir if you may have directories whose name starts with -.

In zsh, you can do it in a single command with the e glob qualifier:

mkdir *(/e\''REPLY=($REPLY/{red,blue})'\')

but it's quicker to type this as two commands:

d=(*(/)); mkdir $^d/{red,blue}
2
  • 1
    On a site that is meant to be a reference, I'd rather say "mkdir -- whatever, and omit -- if you intend for the expansion of whatever to be possibly taken as options to the mkdir command.". There's no point in omiting -- here. It may not be a problem in the OP's case, but it is one in the general case. I think we have a duty to teach the correct and safe syntax here. Jan 21, 2013 at 10:35
  • @StephaneChazelas In a scripting context, I always put all necessary -- and quotes and big fat warnings when newlines aren't supported. In a command line context, I prefer to show the quick version that works in practice first, and the bulletproof version as a complement. Jan 21, 2013 at 11:06
1

You can do that like that:

mkdir {a,b,c,d}/tmp1
1
  • 2
    What if he has 100 directories?
    – Bernhard
    Jan 20, 2013 at 20:13
1

Variation on sputnick's answer which avoids non-directory files:

for x in *; do if [ -d "$x" ]; then mkdir "$x/tmp1"; fi; done
2
  • I treat only dirs, that"s all the magic... Jan 20, 2013 at 15:40
  • cool, got it ;)
    – goldilocks
    Jan 20, 2013 at 15:44
1

This is a good one from the old Unix Masters (tested on FreeBSD 10.1) if you want to version control (with RCS check in locked: ci -l with a brief comment as ir: for initial release) your /etc/* config files on ~/.his/etc

sudo rsync -av /etc/ ~/.his
sudo find -d ~/.his/etc -type d -exec echo '{}/RCS' \; | xargs sudo mkdir -p
sudo find -d ~/.his/etc \! -name "RCS" | xargs sudo ci -l -mir -t-ir
1
  • I fail to see how this answer the OP
    – Archemar
    Mar 28, 2015 at 7:07
1

Should be \! name "RCS" and it works best on the first check in.

sudo find ~/.his/etc \! -name "RCS" -ls

sudo find -d ~/.his/etc \! -name "RCS" | xargs sudo ci -l -mir -t-ir

This last one also works very well for the cjeck in part filtering all RCS content on the file name:

sudo find -d ~/.his/etc -type f | grep -v RCS | xargs sudo ci -l -mir -t-ir

1

this is a great help to me. i had a need to create a set of subdirectories in multiple folders and something like:

!/bin/sh

cd /dest/cont

for dir in */*/; do
    mkdir -p -- $dir/{FB,Video,Audio,proj};
done

worked great except when a white space is encountered. is there a way to ignore spaces in the */*/ part of the arguments?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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