9

I have a bunch of folders and I want to loop over all those folders and in each folder create another folder named "Photo"

F1
--F11
----F111
----F112
--F12

I want to have:

F1
--Photo
--F11
----Photo
----F111
------Photo
----F112
------Photo
--F12
----Photo
8

From the directory just above F1 run this script:

The purpose of the script is to avoid having redundant photo directories created under photo directories.

Also, it's design to prevent accidentally running it from a wrong location such as the user's root directory, whereas it wouldn't see the F1 directory and mistakenly create hundreds of photo directories in the wrong palaces.

Also, it was my intentions to make an easy to follow script so the user could customize it.

#!/bin/bash

while IFS= read -r -d '' i;
do
    if [[ ! -d $i/Photo ]]; then
        # Filter to skip folders named Photo
        thisfolder="$(echo $i | sed "s/.*\///")"
        if [[ ! $thisfolder == "Photo" ]]; then
            mkdir "$i/Photo"
        fi
    fi
done < <(find F1 -type d -print0)

Script Updated:

  • Changed the lower case p in photo to upper case.
  • Added support for non-stander filesnames (with suggestions from Terdon).
  • 1
    I'd definitely avoid parsing ls, it can fail in unusual ways. I think the answers that avoid this are probably safer. – Arronical Sep 14 '16 at 9:30
  • 3
    @Arronical ls isn't parsed. It's just feedback to the user... showing what is happening. Just like the words "This is a photo directory". It's letting the user know why it's not creating a directory there. – L. D. James Sep 14 '16 at 9:34
  • 1
    I had thought that might be the case, as I couldn't see anything being done with it. In that case, I retract my comment and slope off :) – Arronical Sep 14 '16 at 9:49
  • find -type d F1, maybe? – Olivier Grégoire Sep 14 '16 at 11:28
  • Note that this will fail if there's a file (and not a directory) called $i/Photo, but the OP hasn't specified what should be done in such a case. It will also fail in the, admittedly unlikely case, where one of the $is contains a newline. – terdon Sep 14 '16 at 14:04
24

You can achieve it using a single find call.

find . -name 'Photo' -prune -o -type d -exec mkdir {}/Photo \;

The part -name 'Photo' -prune -o tells to not recurse to existing (and newly created) Photo directories: If the found file (or directory) has the name Photo, stop processing it (-prune). In the other case (-o), continue with directories (-type d) and execute (-exec) the command.

  • ah nice, I tried it that way and I see from your answer where I was going wrong :) – Zanna Sep 14 '16 at 9:16
  • @Zanna -prune? :) Yes, it’s quite counter-intuitive but worth learning. – Melebius Sep 14 '16 at 9:22
  • Will it stop creating a folder 'Photo' if a file named 'Photo' exists? – Anwar Sep 14 '16 at 9:40
  • 3
    @Anwar The current version will. There is a grey zone in requirements. :) You can change this by specifying -type d together with -name 'Photo' before -prune. – Melebius Sep 14 '16 at 9:43
  • 2
    @Anwar At second glance, it won’t make sense. You cannot create a folder with the same name as a regular file within the same folder. And find will continue with other folders (including subfolders of that folder, if you are asking for that). – Melebius Sep 14 '16 at 10:31
9

You could pass find results to while instead of for since this way it won't break if the filenames have spaces

First cd to the directory F1, then do:

find -type d | while read dir ; do mkdir "$dir"/Photo ; done

If the directory exists, mkdir will just refuse to create it, so no problem there...

If your directory names could contain newlines, or other exotic strangeness, use this which can deal with arbitrary names:

find -type d -print0 | while IFS= read -r -d '' dir ; do mkdir "$dir"/Photo ; done

Thanks to @terdon for help with this :)

  • If the op's F1 directory is ~user/Pictures, moving to the parent directory and running the line will put a Photo directory in every directory in his space, which could be thousands. Consider adding the F1 as part of the commanline to avoid an easily made mistake. That way if it's inadvertently run from the wrong place, it won't create directories. – L. D. James Sep 14 '16 at 12:18
  • ah yeah I actually was unsure of their directory structure. I suggest doing it from F1 instead, edited @L.D.James – Zanna Sep 14 '16 at 12:23
5

Python with os.walk can do it fairly easily and in only a few lines of code

$ mkdir dir_one dir_two
$ mkdir dir_two/dir_three
$ python -c 'import os;[os.mkdir(r + "/Pictures") for r,d,f in os.walk(".")]'                 
$ tree
.
├── dir_one
│   └── Pictures
├── dir_two
│   ├── dir_three
│   │   └── Pictures
│   └── Pictures
└── Pictures

Explanation

What we're dealing with is this command: python -c 'import os;[os.mkdir(r + "/Pictures") for r,d,f in os.walk(".")]'

Here we simply call python interpreter with -c flag to specify the command we give within single quotes. os.walk() function allows us to recursively step through all subdirectories, and for each subdirectory (represented by r ) , we create Pictures folder via os.mkdir() function. r+"/Pictures" is just a simple string manipulation to join subdirectory string and "/Pictures" string together to form one path

Replace "/Pictures" with "/Photo" to fit your requirements

4

If you're using bash, you can simply run:

shopt -s globstar
for d in **/; do mkdir -p "$d"/Photo; done

The globstar option makes ** match "all files and 0 or more directories and subdirectories". In other words, it matches everything recursively. Because of the / at the end, **/ will match all directories and subdirectories (no files). Therefore, the $d will iterate over all directories in the current directory and their subdirectories and the Photos directory will be created in each of them.

The -p flag of mkdir means:

  -p, --parents
          no error if existing, make parent directories as needed

Here, I'm just using it to suppress error messages if the target directory already exists. Remove it if you want to see the errors.

Note that this will fail to create a Photo subdirectory in a target directory that contains a file named Photo. You haven't specified what should be done in those cases though and all other answers posted so far also have this limitation.

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.