-3

I read that it is a best practice to double quote all of your variable expansions in Bash. I also read that one cannot use a shell glob (wildcard(*)) right after a double quoted variable expansion. These situations conflict when one wants use the value of a regex pattern, expanded out of a variable.

The reason I would sometimes want to combine regex with a wildcard is to keep my regex minimal, neater, in my personal taste.

My particular problem

I downloaded phpmyadmin into my document root directory and unzipped it, but I fail to rename it with mv by a regex pattern I put in a variable, and available when expanding its variable. Here's the exact trace:

userName@compName:/var/www/html# ll
total 11336
drwxr-xr-x  3 root root     4096 Feb 14 07:04 ./
drwxr-xr-x  3 root root     4096 Feb 14 06:56 ../
-rw-r--r--  1 root root      612 Feb 14 06:57 index.nginx-debian.html
drwxr-xr-x 12 root root     4096 Dec 23 08:50 phpMyAdmin-4.7.7-all-languages/
-rw-r--r--  1 root root 11589684 Dec 23 14:08 phpMyAdmin-latest-all-languages.zip
userName@compName:/var/www/html# echo $pma
[pP][hH][pP][mM][yY][aA][dD][mM][iI][nN]
userName@compName:/var/www/html# mv "$pma"*/ phpmyadmin/
mv: cannot stat '[pP][hH][pP][mM][yY][aA][dD][mM][iI][nN]*/': No such file or directory

If I'll unquote the variable expansion to ${pma} I would indeed be able to combine the variable expansion and the regex, as in ${pma}*, but it is important to me to follow the best practice without exceptions, if I can.

My question

How could I keep the variable expansion double quoted, but still using the extracted value with a wildcard?

  • See unix.stackexchange.com/questions/68694/… for that. – muru Feb 14 '18 at 7:35
  • why not just extract and keep the filename from the URL when you download it? BTW, the regexp is probably never going to be needed - it's extremely unlikely that the phpMyAdmin part of the filename will ever change any letters for upper to lower case or vice-versa. – cas Feb 14 '18 at 8:25
  • also, you seem to be using debian (guessing from the index.nginx-debian.html filename), why not just use the phpmyadmin package in debian? Essential reading: wiki.debian.org/DontBreakDebian – cas Feb 14 '18 at 8:30
  • 1
    @muru: What do you mean, “Shell globs are not wildcards.”?  I use the terms interchangeably, and so do Gilles, Stéphane Chazelas, Jeff Schaller, our wildcards tag wiki, … (Cont’d) – Scott Feb 18 '18 at 21:18
  • 1
    @Scott of course, it doesn't help that my previous comment linking to another U&L post talking about regexes and globs was deleted. Wish mods would delete all comments in a conversation instead of this piecemeal nonsense. – muru Feb 19 '18 at 0:01
6

A filename globbing pattern kept in a variable will not glob filenames if double quoted. It is the double quoting that stops the filenames from being globbed, not the * wildcard at the end or the combination of quoting and *.

We often tell users on the site to "quote their variables", and we do so because the values of unquoted variables undergo word splitting and file name globbing, and this is usually not what's wanted. For example, a password may be [hello] world* and on a command line containing -p $password that would do "interesting" things depending on what files were present in the current directory (and it may well not work at all due to the space). See also the question "Security implications of forgetting to quote a variable in bash/POSIX shells"

What you want to do here is the opposite of what we usually want to avoid, namely invoking file name globbing using the file name globbing pattern in your variable.

If you truly can not rely on the name of the extracted directory to remain stable, a better (as in "cleaner") solution would possibly be to make sure that it is the only thing in a temporary directory, and then just use * to move it into place (possibly changing its name in the process, so that you know for sure what it is called).

This is better than simply removing the quotes around your variable, as the filename globbing pattern might match other names than the single one that you expect, depending on what other things are available in the directory.

This is a variation of my answer to your previous question:

#!/bin/sh -ex

destdir="/var/www/html/phpmyadmin"

tmpdir=$(mktemp -d)
trap 'rm -rf "$tmpdir"' EXIT    # remove temporary directory on termination

wget -O "$tmpdir/archive.zip" \
    "https://www.phpmyadmin.net/downloads/phpMyAdmin-latest-all-languages.zip"

cd "$tmpdir" && {
    unzip archive.zip
    rm -f archive.zip

    # The only thing in the current (temporary) directory now is the
    # directory with the unpacked zip file.

    mkdir -p "$destdir"
    mv ./* "$destdir"/phpmyAdmin
} 

The above may be distilled into five steps:

  1. Create an empty directory (and change current working directory to it).
  2. Fetch the archive.
  3. Extract the archive.
  4. Remove the archive.
  5. Change the name of the lone folder now in the otherwise empty directory.
  • This surly answers the question but I just want to say that I find the sentence What you want to do here is the opposite of what we usually want to avoid, namely accidentally invoking file name globbing. a bit confusing because it isn't preceded by a short definition of what we usually want to avoid (mistakenly globbing variables I assume). I think it should be changed a bit. Also, maybe it's worth mentioning $x or ${x} as exceptional alternatives. – user9303970 Feb 14 '18 at 14:29
  • 1
    @user9303970 Agreed. I have added text. – Kusalananda Feb 14 '18 at 14:39
0

How could I keep the variable expansion double quoted, but still using the extracted value with a wildcard?

You couldn't1, and you don't want to; your command works perfectly well without them (and I'm fairly sure that was pointed out in one of the earlier-deleted variants of this question, too).

$ mkdir phpMyAdmin-4.7.7-all-languages
$ pma=[pP][hH][pP][mM][yY][aA][dD][mM][iI][nN]
$ echo $pma
[pP][hH][pP][mM][yY][aA][dD][mM][iI][nN]
$ echo $pma*/
phpMyAdmin-4.7.7-all-languages/
$ mv $pma*/ phpmyadmin
$ ls
phpmyadmin

The point of quoting your variables is to ensure that shell globbing and field splitting don't happen. This time you actually want that to happen, so quoting is counterproductive.

It's worth understanding why these guidelines exist; they're not blanket rules, and if they're not working for you then you might be in a situation where they don't apply. Striving "to follow the best practice without exceptions" without understanding them is totally wrong.


Aside from that, this:

I also understood that one cannot use a shell glob (wildcard(*)) right after a double quoted variable expansion.

is not true.

$ tmp=php
$ echo "$tmp"*
phpmyadmin

1 You could use eval, but it would be so utterly pointless that I'm not going to follow that up any further.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.