The following code is working:

mkdir /home/karan/{Pictures,Public}/yo

The above line creates a yo named directory in the directories named Public and Pictures. However when I use the below code I get an error:

mkdir /home/karan/{P*}/yo
error: cannot create directory ‘/home/karan/{P*}/123’

I know that the asterisk is not expanding into the given directories but cannot understand the reason. I also haven't used any quoting mechanisms to suppress the asterisk expansion.


With zsh, you can do:

(){ mkdir $^@/yo; } /home/karan/P*(/)

(where $^array turns on rcexpandparam (for rc-style array expansion) for the expansion of $array only, and the expansion of that glob (here with a / glob qualifier to restrict to files of type directory) is turned into the $@ array through the use of an anonymous function).

Or you could do:

mkdir /home/karan/P*(/e:REPLY+=/yo:)

(using the evaluate glob qualifier to append a /yo to matching files).


set -o histsubstpattern
mkdir /home/karan/P*(/:s:%:/yo)

(using the :s/string/replacement/ csh-style modifier to add /yo in that case. With histsubstpattern, the string is interpreted as a pattern and like in ksh's ${var/pattern/replacement}, a leading % means the pattern is anchored at the end of the subject)

With rc or derivatives:

dirs = ( /home/karan/P*/ )
mkdir $dirs^yo

es, one of those rc derivatives also has anonymous functions like zsh above, though with a different syntax:

@ {mkdir $*^yo} /home/karan/P*/

With fish:

set dirs /home/karan/P*/
mkdir ${dirs}yo

With ksh93 or bash (or zsh), you could always do:

mkdir "${dirs[@]/%/yo}"

Or with any shell and GNU xargs:

printf '%syo\0' /home/karan/P*/ | xargs -r0 mkdir

(beware that the expansion of P*/ (as opposed to zsh's P*(/)) would also include symlink to directories. In zsh, replace the / glob qualifier with -/ if its actually what you want).

  • I would expect mkdir "${dirs[@]/%/yo}" to trigger ${parameter/pattern/replacement}. Doesn't it? – Quasímodo Oct 31 '20 at 12:10
  • 1
    @Quasimodo, yes and if the pattern starts with %, that anchors it at the end of the subject. When applied to array[@], that applies to all elements of the array. – Stéphane Chazelas Oct 31 '20 at 12:12

Bash's manual:

Patterns to be brace expanded take the form of an optional preamble, followed by either a series of comma-separated strings or a sequence expression between a pair of braces

Your attempt

  1. mkdir /home/karan/{P*}/123

is not a valid form of brace expansion, since there are no commas nor a sequence inside the braces. Thus, you get exactly what you have given, /home/karan/{P*}/123.

  1. mkdir /home/karan/{P*,}/123

is a brace expansion (note the comma) which would try to create /home/karan/P*/123 and /home/karan//123 directories. Note that still P* is not expanded with filename expansion, because there are yet no .../Pictures/123 and .../Public/123 directories. In other words, when there is no match, filename expension does not happen.

Anyway, as (1) shows, it does not make sense to use a brace expansion with a single element, it is like a seesaw with a single person. The direct

  1. mkdir /home/karan/P*/123

would fail for the same reason, no match.

I don't see a nice way to escape spelling the paths out in your particular case, but if there are many matching directories or paths, a for loop will save you keystrokes.

for d in /home/karan/P*/; do mkdir "$d"yo; done

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.