Bash wildcard not expanding

A wildcard always expands to existing names.

Your command mkdir * fails because the names that * expands to already exists.

Your command mkdir *.d "fails" because the *.d does not match any existing names. The pattern is therefore left unexpanded by default1 and a directory called *.d is created. You may remove this with rmdir '*.d'.

To create a directory for each regular file in the current directory, so that the new directories have the same name as the files, but with a .d suffix:

for name in ./*; do
    if [ -f "$name" ]; then
        # this is a regular file (or a symlink to one), create directory
        mkdir "$name.d"
    fi
done

or, for people that like "one-liners",

for n in ./*; do [ -f "$n" ] && mkdir "$n.d"; done

In bash, you could also do

names=( ./* )
mkdir "${names[@]/%/.d}"

but this makes no checks for whether the things that the glob expands to are regular files or something else.

The initial ./ in the commands above are to protect against filenames that contain an initial dash (-) in their filenames. The dash and the characters following it would otherwise be interpreted as options to mkdir.


1 Some shells have a nullglob shell option that causes non-matched shell wildcards to be expanded to an empty string. In bash this is enabled using shopt -s nullglob.


*.d expands to the files whose name end in .d.

With zsh, you could do:

files=(*)
mkdir -- $^files.d

Or with an anonymous function:

() { mkdir -- $^argv.d; } *

Or adding the suffix via the e glob qualifier:

mkdir -- *(e{REPLY+=.d})

Or the :s history modifier applied to globs (with histsubstpattern for % to mean end):

set -o histsubstpattern
mkdir -- *(:s/%/.d)

You may also want not to do that for files that are already directories or symlinks to directories, by adding a ^-/ glob qualifier:

files=(*(^-/))
mkdir -- $^files.d

(note that zsh doesn't have that misfeature of other Bourne-like shells where patterns that don't match are passed as-is, so mkdir *.d would not create a *.d directory if there was no file matching *.d in the current directory, it would abort the command with an error instead)


Instead of for foo in bar; do baz; done solution I would propose:

find . -maxdepth 1 -type f -print0|xargs -0 -I file mkdir file.d