Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

I have something like this:

% ls -1dF /tmp/foo/*
/tmp/foo/000f9e956feab3ee4625aebb65ae7bae9533cdbc/
/tmp/foo/002e34c2218f2c86fefd2876f0e5c2559c5fb3c4/
/tmp/foo/00b483576791bab751e6cb7ee0a7143af43a8069/
.
.
.
/tmp/foo/fedd0f7b545e7ae9600142656756456bc16874d3/
/tmp/foo/ff51ac87609012137cfcb02f36624f81cdc10788/
/tmp/foo/ff8b983a7411395344cad64182cb17e7cdefa55e/

I want to create a directory bar under each of the subdirectories under foo.

If I try to do this with

% mkdir -p /tmp/foo/*/bar

...I get the error

zsh: no matches found: /tmp/foo/*/bar

(In hindsight, I can understand the reason for the error.)

I know that I can solve the original problem with a for-loop, but I'm curious to know if zsh supports some form of parameter expansion that would produce the desired argument for a single invocation of mkdir -p. IOW, a parameter expansion equivalent to "append /bar to every prefix generated by expanding /tmp/foo/*", resulting in

% mkdir -p /tmp/foo/000f9e956feab3ee4625aebb65ae7bae9533cdbc/bar ... /tmp/foo/ff8b983a7411395344cad64182cb17e7cdefa55e/bar
share|improve this question
up vote 13 down vote accepted
setopt histsubstpattern extendedglob
mkdir -p /tmp/foo/*(#q/:s_%_/bar_)

This is extended globbing that has a quiet glob flag that uses a glob qualifier to match only directories and a modifier to perform a substitution (using the % pattern character that is only available in history substitution pattern mode) that appends a string to each word.

man zshexpn
share|improve this answer
    
Wow, I'm impressed. I even found it all in the man page you specify. – Wildcard yesterday
1  
Note that the Original Poster specified mkdir -p, which would have the slightly different effect from your command of not throwing errors if some of those directories already have bar subdirectories. Minor point but worth noting here. :) – Wildcard yesterday
1  
Another way is to use array expansion via ${^spec} and append bar to each element of whatever /tmp/foo/*(/) expands to: set -- /tmp/foo/*(/) then mkdir -p -- "${^@}/bar" – don_crissti yesterday

Sure - use a loop

for n in /tmp/foo/*; do mkdir "$n/bar";done

globs are used to expand lists of existing items, not things which have not yet been created.

share|improve this answer
    
Sorry, I'm afraid my question was not clearly stated. I will clarify it. – kjo yesterday
2  
This doesn't answer if zsh can expand globs and append text with a single syntax—but I do think it's likely the only way to do what you want, @kjo. – Wildcard yesterday

If the directories are in an array, you can use the ${^...} form of expansion.

a=(/tmp/foo/*/)
mkdir -p ${^a}bar
share|improve this answer

You can use find and xargs to do it:

find /tmp/foo -maxdepth 1 -mindepth 1 -type d | xargs -i echo mkdir -p {}/bar

which uses -maxdepth 1 and -mindepth 1 to prevent creation of bar in subdirectories of targets, and in /tmp/foo itself respectively. type d makes it consider directories only.

You have to remove echo to perform actual actions, otherwise echo will only print out lines to execute. You can also enclose it in substitution like $() to execute it.

Manual says xargs -i is deprecated, but I found no other equally handy alternative, so it should still work for some years... You can replace -i with -I'{}' to be up-to-date with that.

share|improve this answer

@loa_in_ had most of it, but piping find output tends to be a faux pas.

find /tmp/foo -maxdepth 1 -mindepth 1 -type d -exec mkdir {}/bar \;
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.