GIT_DIR=$HOMEDIR/AppData/Local/GitHub/PortableGit*/bin
Above the wildcard is not expanded, because you're assigning to a scalar variable which can only contain one string, so no word splitting or filename generation take place there.
eval GIT_DIR_PASS=$GIT_DIR
Here, it's a an argument to a simple command, so wildcards are expanded but they would be expanded to files whose path is "GIT_DIR_PATH=..."
.
echo $GIT_DIR_PASS
Here the wildcards are expanded. You're not printing the content of the variable, but the list of arguments that result from the expansion since the variable is not quoted.
You'd want:
set -- "$HOMEDIR"/AppData/Local/GitHub/PortableGit*/bin
That is expand that pattern into the list of matching files and assign them to $1
, $2
...
GIT_DIR=$1
Pick the first one.
The shell expands *
only if un-quoted, any quoting stops expansion by the shell.
Also, a brace expansion needs to be unquoted to be expanded by the shell.
This work (lets use echo to see what the shell does):
$ echo *.{ext1,ext2}
a.ext1 b.ext1 a.ext2 b.ext2
Even if there are files with some other names:
$ touch {a,b}.{ext1,ext2} {c,d}.{ext3,ext4} none
ls
a.ext1 a.ext2 b.ext1 b.ext2 c.ext3 c.ext4 d.ext3 d.ext4 none
$ echo *.{ext1,ext2}
a.ext1 b.ext1 a.ext2 b.ext2
Why that works?
It is important that we understand why that works. It is because of the order of expansion. First the "Brace expansion" and later (the last one) "Pathname Expansion" (a.k.a glob-expansion).
Brace --> Parameter (variable) --> Pathname
We can turn off "Pathname expansion" for a moment:
$ set -f
$ echo *.{ext1,ext2}
*.ext1 *.ext2
The "Pathname Expansion" receives two arguments: *.ext1
and *.ext2
.
$ set +f
$ echo *.{ext1,ext2}
a.ext1 b.ext1 a.ext2 b.ext2
The problem is that we can not use a variable for the brace expansion.
It has been explained many times before for using a variable inside a "Brace Expansion"
To expand a "Brace Expansion" that is the result of a "Variable Expansion", you need to re-submit the command line to the shell with eval
.
$ list={ext1,ext2}
$ eval echo '*.'"$list"
Brace --> Variable --> Glob || --> Brace --> Variable --> Glob
........ quoted here -->^^^^^^ || eval ^^^^^^^^^^^^^^^^^^^^^^^^^
Values of the file names bring no execution problem for eval:
$ touch 'a;date;.ext1'
eval echo '*.'"$list"
a;date;.ext1 a.ext1 b.ext1 a.ext2 b.ext2
But the value of $list
could be unsafe. However, the value of $list
is set by the script writer. The script writer is in control of eval
: Just not use externally set values for $list
. Try This:
#!/bin/bash
touch {a,b,c}.ext{1,2}
list=ext{1,2}
eval ls -l -- '*.'"$list"
A better alternative.
An alternative (without eval) is to use Bash "Extended Patterns":
#!/bin/bash
shopt -s extglob
list='@(ext1|ext2)'
ls -- *.$list
Note: Please be aware that both solutions (eval and patterns) (as written) are safe for filenames with spaces or new lines. But will fail for a $list
with spaces, because $list
is unquoted or the eval removes the quotes.
Best Answer
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 withrmdir '*.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:or, for people that like "one-liners",
In
bash
, you could also dobut 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 tomkdir
.1 Some shells have a
nullglob
shell option that causes non-matched shell wildcards to be expanded to an empty string. Inbash
this is enabled usingshopt -s nullglob
.