Bash wildcard not expanding

7,927

Solution 1

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.

Solution 2

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

Solution 3

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

Share:
7,927
Gooberpatrol66
Author by

Gooberpatrol66

Updated on September 18, 2022

Comments

  • Gooberpatrol66
    Gooberpatrol66 almost 2 years

    I'm trying to make a directory for each file in a directory.

    mkdir *
    

    returns File exists. So I try

    mkdir *.d
    

    and it makes a directory called "*.d". How do I force the wildcard to expand?

    • user1934428
      user1934428 about 6 years
      Consider setting the failglob option in your .bashrc file. This would have warned you when you did the mkdir *.d.
  • Danielle
    Danielle about 6 years
    It is bad idea to use for name in ./*. Instead I would go with find . -depth 1 -and -type f -exec mkdir '{}.d' \; which would work even if you have non-files in current directory.
  • Kusalananda
    Kusalananda about 6 years
    @Hauleth In what way is it a bad idea? I'm testing each pathname before creating the directories, just like find would do. You find command would probably use -maxdepth 1 rather than -depth 1 (-depth does not take an argument) if -maxdepth is supported, and -and is not needed (and would make it non-portable). For a portable find variant: find . -type f -exec mkdir {}.d ';' -o ! -name . -type d -prune, but that would also create directories for hidden files.
  • Kusalananda
    Kusalananda about 6 years
    @Hauleth ... (and it would still depend on whether the find implementation expands {}.d to the pathname concatenated with .d, which it isn't required to do).
  • Kusalananda
    Kusalananda about 6 years
    If you'd like to avoid creating hidden directories (corresponding to hidden files), also use ! -name '.*'.
  • done
    done about 6 years
    To select only directories (in the bash solution) use names=( ./*/ ).
  • Kusalananda
    Kusalananda about 6 years
    @Isaac Yes, but in this case we would ideally only want to select regular files. That's why I also mentioned that caveat.