0

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.

2 Answers 2

3

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).

Or:

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:

dirs=(/home/karan/P*/)
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).

2
  • I would expect mkdir "${dirs[@]/%/yo}" to trigger ${parameter/pattern/replacement}. Doesn't it?
    – Quasímodo
    Commented Oct 31, 2020 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. Commented Oct 31, 2020 at 12:12
1

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
3
  • "it is like a seesaw with a single person" what a sad image. Commented Oct 31, 2020 at 15:41
  • @glennjackman I thought a carriage without horses could suggest a subordinate relation so I ditched it. I need to improve my analogies repertoire.
    – Quasímodo
    Commented Oct 31, 2020 at 21:37
  • Related - Filename pattern for files that don't yet exist Commented Nov 1, 2020 at 8:22

You must log in to answer this question.

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