10

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
kjo
  • 15,339
  • 25
  • 73
  • 114

5 Answers5

13
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
jpaugh
  • 329
JdeBP
  • 68,745
  • Wow, I'm impressed. I even found it all in the man page you specify. – Wildcard Oct 11 '16 at 10:17
  • 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 Oct 11 '16 at 10:18
  • 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 Oct 11 '16 at 10:21
5

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.

Thomas Dickey
  • 76,765
  • Sorry, I'm afraid my question was not clearly stated. I will clarify it. – kjo Oct 11 '16 at 08:35
  • 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 Oct 11 '16 at 08:37
4

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

a=(/tmp/foo/*/)
mkdir -p ${^a}bar
chepner
  • 7,501
0

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.

loa_in_
  • 398
  • 1
  • 9
  • This answer and the next one look good to me. Can someone explain why they should be down voted? Down votes are fine, but how can we learn if no reasons are given? – Joe Oct 15 '16 at 03:27
  • It is a working alternative doing nothing but what OP asked. Beats me. – loa_in_ Oct 15 '16 at 10:34
  • I think it's some kind of bias that answers must have bad answers on them, so prejudice took it's toll on my reputation. – loa_in_ Oct 15 '16 at 10:36
  • I don't like unexplained down votes, but I haven't seen any such bias. Usually, "bad" answers (the ones that I understand) are actually bad. – Joe Oct 17 '16 at 10:57
0

@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 \;
Sammitch
  • 334
  • I just use xargs with different tools which don't have -exec alternative, so I use my approach as it works with all of them equally. – loa_in_ Oct 15 '16 at 10:38