2

My folder tree

.
|-- 1
|-- 2
|-- 3
...
|-- 777

I would like to create the folder pictures to each folder.

I run unsuccesfully

mkdir */pictures

One way is of course creating 777 mkdir -commands with Vim's regex. However, I would know how you can do that in shell.

How can you mkdir */pictures?

1
  • 1
    I don't know, does bash scripting count as programming? Jul 30, 2009 at 22:35

9 Answers 9

8
for x in `seq 1 777`
do
    mkdir $x/pictures;
done

Also, in the past you have mentioned zsh and this should work without modification under at least bash and zsh.

7

Almost the same as others, but adding a slash, so it only counts directories and not regular files

for x in */
do
    mkdir $x/pictures;
done
2

Many ways to do this, I think this is the simplest:

for f in *; do mkdir $f/pictures; done

This is quick and dirty and will make a subdirectory for everything in your current directory. You will get harmless errors if there are files in the current working directory. If that bothers you a more complex solution using find or seq or the like is better. If your directories are numbered then a simpler version of Sean's seq example (in bash) is

mkdir {1..777}/pictures

BTW, mkdir can easily make several directories at once:

mkdir 1/pictures 2/pictures

The problem is that */pictures doesn't expand to anything in the shell since the directories don't exist yet.

2
for i in *; do cd $i; mkdir pictures; cd ..; done

Edit: I noticed that someone beat me to the punch so I'll improve the script a bit (test whether it is a directory):

for i in *; do if [ -d $i ]; then cd $i; mkdir pictures; cd ..; fi; done
2
  • 1
    No need to change directories. for i in *; do if [ -d $i ]; then mkdir $i/pictures; fi; done Jul 31, 2009 at 3:19
  • 1
    for i in *; do if [ -d "$i" ]; then mkdir "$i/pictures"; fi; done <p>Quote the shell vars, because it's the way we do things. Jul 31, 2009 at 3:29
1

I'm going to assume you're using bash...

find . -mindepth 1 -maxdepth 1 -type d | while read dir; do mkdir $dir/pictures; done

or if everything in the directory is a directory

ls | while read dir; do mkdir $dir/pictures; done

If you're using tcsh, you have to do this with an inelegant for loop.

1
mkdir ~/Pictures
mkdir ~/container
mkdir ~/container/1
mkdir ~/container/2
mkdir ~/container/3
mkdir ~/container/4
mkdir ~/container/5

cp -r ~/Pictures ~/container/**/
rm -rf ~/Pictures

This will do the trick, i've just tried it

2
  • Which OS do you use? - I could not get your series of commands to work. - I run your command in Bash and Zsh in OS X Leopard. Jul 30, 2009 at 23:13
  • Wow really late reply here but i'm on debian linux in normal bash shell May 5, 2011 at 15:46
1

How about:

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

My recomended solutions:

find . -mindepth 1 -maxdepth 1 -type d '(' -exec mkdir -- '{}'/pictures \; -o -exec printf 'Could not create: [[%s/pictures]] ??\n' '{}' \; ')';

or

(for d in ./* ; do [ -d "$d" ] && { mkdir -- "$d"/pictures || printf 'Could not create [[%s/pictures]]... permissions problem, disk is full?\n' "$d";};done;);

or

(for d in   * ; do [ -d "$d" ] && { mkdir -- "$d"/pictures || printf 'Could not create [[%s/pictures]]... permissions problem, disk is full?\n' "$d";};done;);

The following is an example:

(for d in ./* ; do [ -d "$d" ] && { mkdir    "$d"/pictures || printf 'Could not create [[%s/pictures]]... permissions problem, disk is full?\n' "$d";};done;);

This last one will work, but i don't recommend it, it is here to show how using ./* instead of * can help reduce risks in some cases as the expansions for ./* will be the same as for * but prefixed with ./ so it they are valid paths also and even if any of those values is used carelessly as in mkdir "$d"/pictures, it will cause no problems (or less problems at least, depending on the context).

All of them will work with names containing (space), tabs, CR, LF... ANY CHARACTER in names (except /, and that's because it's not allowed in names, only to separate one name from the other in the path, and as root directory ). So basically, it will work with any path.

To every one that have read the answers that uses variables for file system names: don't follow those examples, at the time of writing all are wrong, and there is a common problem in all of them: the variables are not quoted but in all cases here they should. (If there isn't any problematic character in the names, it will do the same, but even in that case it's good practice to quote them).

But this does not apply only to variables containing file system names, every single variable substitution should be quoted when possible.

There are very few circumstances in which one needs not to quote a shell variable; and in all of them variable contents should be triple-checked, in that situations you can't be too careful. Those circumstances are very rare and can be avoided almost always.
There is a exception to this: Here-documents, substitutions inside them behave like they are already quoted, substitutions inside substitutions inside here-documents behave as normal.

(*) I've already seen this particular cancer spread enough, and too many people asking why does a command break only in some cases/paths... and being 'helped' by rewriting half of the script when a pair of " would have fixed the offending line.
I think i'm gonna get 'I quote vars and substitutions like there's no tomorrow!' printed on a t-shirt.).


At the time of writing, and IMHO there is not a single acceptable (acceptable without objections) answer to the question, and that's unacceptable!
In the last line of the question, OP asked how to mkdir */pictures and i take that as a petition for a generic solution that does not make assumptions on names of subdirectories.

The second code block from the answer by Nelson ( mkdir {1..777}/pictures; ) works and doesn't contain any errors nor bad practices (if we don't count the forcing of the use of bash as one xD), but will only work in the EXACT scenario described in the first part of the question and only in bash or some other shell implementing {n..m} brace expansion for sequence-based generation of multiple args from one. The first code block in that answer only deserves to be ignored, it is awfully wrong.

The one by mdpc, edited by Gareth is quite acceptable, almost perfect :

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

despite the fact that using it as-is could end in massacre; but it can be made perfect by modifying it so it looks like:

find . -mindepth 1 -maxdepth 1 -type d -exec mkdir -- '{}'/pictures \;

(It is the first in the list of solutions in my anwer).
It includes -- between the options (flags) for mkdir (or mkdir itself) and the argument:
It's good practice to always do that when the start character of the first argument is not hardcoded into the command to keep it from trying to parse the argument as a potential option(flag). This can be done with almost all commands (there are a few exceptions, find itself is an obvious one).
Note that i quoted {}, its more correct to use '{}', or \{\}, since that way it won't have any special meaning for any posix shell even crappy ones.

In short: only two solutions don't contain errors, but one uses a "bashism" so won't work in many shells, and the other could have been more correct (not good enough for the people to correctly learn script-fu from), quite disappointing. I had too high expectations i guess...

I'm not saying that no answer will work for THAT EXACT scenario, most will, but definitely most of them aren't correct code (i am not referring to syntax, not only at least) and/or include bad practices of some kind (some of them even real atrocities).


Sources: A kind of obsession about standards and correctness of code, and documentation from http://www.opengroup.org/, man dash, man bash and even the internet deeps! : but trusted sources, not answers from dont-even-know-how-to-quote-correctly-guys... (there are at least couple of answers that looks like that in this question, no offense intended, no one is born knowing everything).

0

I tested it out and this should be what you need:

#!/bin/bash 
list=$(/bin/echo *)

for file in $list
do
    if [ -d $file ]; then
        cd $file
        mkdir picture
        cd ..
        echo "Added folder to $file"
    fi
done

You must log in to answer this question.

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