Braces
Quoting the subsection 2.6.2 Parameter Expansion of the POSIX Shell & Utilities:
The parameter name or symbol can be enclosed in braces, which are optional except for positional parameters with more than one digit or when parameter is a name and is followed by a character that could be interpreted as part of the name.
What this means is:
- To use the positional parameters (
$0
, $1
, $2
, ...) beyond $9
, you need to use braces: ${10}
.
- When using parameters with characters which could be interpreted as part of the name, like in your example:
$prefix2
, braces are used to explicitly specify that the character (2
) should not be considered part of the name. So, $prefix2
expands into whatever is the contents of prefix2
, while ${prefix}2
expands into whatever is the contents of prefix
parameter with 2
added. Example:
$ prefix=Mr.
$ prefix2=Mrs.
$ printf "%s Smith\n" $prefix2
Mrs. Smith
$ printf "%s Smith\n" ${prefix}2
Mr.2 Smith
Another example in Bash, where braces do the opposite from the previous example:
$ fruits=(apples bananas oranges)
$ printf "%s\n" $fruits[1]
apples[1]
$ printf "%s\n" ${fruits[1]}
bananas
Here, [
is normally not considered to be a part of the name, so adding braces forces it to be considered a part of the name. This is specific to Bash and other shells which support arrays, like mksh, etc (zsh doesn't need braces here); other POSIX shells which don't support arrays, like dash, give an error Bad substitution
when expanding ${fruits[1]}
.
Characters which constitute Names are defined in the 3.216 Name subsection in the POSIX Base Definitions chapter:
In the shell command language, a word consisting solely of underscores, digits, and alphabetics from the portable character set. The first character of a name is not a digit.
So, as far as POSIX is concerned, prefix2
is a name, while fruits[1]
is not a name (in Bash, zsh, and so on, it is, but in Bash it needs braces; in zsh, it does not).
grep and globbing
If the concern is about the grep invocation
grep -q $prefix*.TXT
first, note that Unix-like filesystems are case-sensitive. This means that FILE.TXT, FILE.txt, file.TXT, file.txt and FiLe.TxT are five separate filenames.
Next, due to Pathname Expansion ("globbing"), the command
grep -q file*
entirely depends on whether there are, and how many, files whose names begin with file
:
- If there are no files with names starting with
file
, the literal string file*
will be passed by the shell to grep, which by default searches its stdin. So a command like
echo $something | grep -q file*
will search the contents of the parameter something
for text
beginning with fil
, followed by zero or more e
:
$ something=Hello
$ echo $something | grep file* # grep 'file*'
$ echo $?
1
$ something=fil
$ echo $something | grep file*
fil
$ something=fileeee
$ echo $something | grep file*
fileeee
$
- If there is one file with name starting with
file
, its name will be passed by the shell to grep as a search pattern, and grep will search its stdin for that pattern. This can have unexpected results if the filename contains characters special to regular expressions, like .
, ^
and so on.
$ touch file.txt
$ something=fileeee
$ echo $something | grep file* # grep 'file.txt'
$ echo $?
1
$ something=file.txt
$ echo $something | grep file*
file.txt
$ something=fileatxt
$ echo $something | grep file*
fileatxt
$
- If there are two or more files with names starting with
file
, the name of the first file (in glob(3p) order) will be passed to grep as the search pattern, with all the implications of point 2. above, and grep will search the remaining files with names starting with file
, ignoring whatever is passed to its stdin:
$ touch file.txt
$ echo file.txt >filea.txt
$ echo fileatxt >fileb.txt
$ something=Hello
$ echo $something | grep file* # grep 'file.txt' 'filea.txt' 'fileb.txt'
filea.txt:file.txt
fileb.txt:fileatxt
$
Conclusion
Combining the previous two sections, the answer depends on the number and names of the files in the current directory, as well as the presence of parameter named prefix2
. We can use set -x
to show what is passed by the shell to utilities.
When there are no files ending with .TXT
:
$ file='GLOBAL_AR_COLLECTOR_COMMENTS20240614ALYSSAB.TXT'
$ prefix='GLOBAL_AR_COLLECTOR_COMMENTS'
(Note: there is no space before =
! With space present, the shell word splitting will interpret file
as a command name.)
$ set -x; echo $file | grep $prefix2*.TXT; echo $?; set +x
+ echo GLOBAL_AR_COLLECTOR_COMMENTS20240614ALYSSAB.TXT
+ grep '*.TXT'
+ echo 1
1
+ set +x
$ set -x; echo $file | grep ${prefix}2*.TXT; echo $?; set +x
+ echo GLOBAL_AR_COLLECTOR_COMMENTS20240614ALYSSAB.TXT
+ grep 'GLOBAL_AR_COLLECTOR_COMMENTS2*.TXT'
+ echo 1
1
+ set +x
$
Since there is no prefix2
parameter, $prefix2
expands to an empty string. Now, if we define prefix2
:
$ prefix2=foo
$ set -x; echo $file | grep $prefix2*.TXT; echo $?; set +x
+ echo GLOBAL_AR_COLLECTOR_COMMENTS20240614ALYSSAB.TXT
+ grep 'foo*.TXT'
+ echo 1
1
+ set +x
$ set -x; echo $file | grep ${prefix}2*.TXT; echo $?; set +x
+ echo GLOBAL_AR_COLLECTOR_COMMENTS20240614ALYSSAB.TXT
+ grep 'GLOBAL_AR_COLLECTOR_COMMENTS2*.TXT'
+ echo 1
1
+ set +x
$
Here, GLOBAL_AR_COLLECTOR_COMMENTS2*.TXT
is interpreted as a regular expression and not a glob, so 2*
means "zero or more of the character 2
", not "character 2
followed by any combination of characters". And now, with file with the name from the parameter file
present:
$ touch $file
$ set -x; echo $file | grep $prefix2*.TXT; echo $?; set +x
+ echo GLOBAL_AR_COLLECTOR_COMMENTS20240614ALYSSAB.TXT
+ grep 'foo*.TXT'
+ echo 1
1
+ set +x
$ set -x; echo $file | grep ${prefix}2*.TXT; set +x
+ echo GLOBAL_AR_COLLECTOR_COMMENTS20240614ALYSSAB.TXT
+ grep GLOBAL_AR_COLLECTOR_COMMENTS20240614ALYSSAB.TXT
GLOBAL_AR_COLLECTOR_COMMENTS20240614ALYSSAB.TXT
+ set +x
$
Due to this answer already reaching a considerable length, I've left out the case with several files matching the ${prefix}2*.TXT
glob, but using this method and the information in the section grep and globbing above, you can see how that case would differ from the other cases.
${variable_name}
doesn’t mean what you think it does …