-2

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?

Jeff Schaller
  • 67,283
  • 35
  • 116
  • 255
  • See https://unix.stackexchange.com/questions/68694/when-is-double-quoting-necessary/68748#68748 for that. – muru Feb 14 '18 at 07: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 08: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: https://wiki.debian.org/DontBreakDebian – cas Feb 14 '18 at 08:30
  • @cas fair point. It it indeed extremely unlikely to change, but what if the extremely unlikely happened and it will? About installing directly from the Debian repositories, I thought of that, but the program is downloaded to /usr/share/ and I then have to create a symlink from document root to /usr/share/ that by itself could change any day to some other location, so I prefer to download phpmyadmin directly to document root and be done with it. – Arcticooling Feb 14 '18 at 08:35
  • if it happens, then modify your script. a more important question is "why would /usr/share change if you don't do anything to make it change?" it wouldn't. people have been using packaged web apps like phpmyadmin and wordpress via symlinks to the package files/directories since the 90s when web apps first started appearing and then started being packaged. it's a tried and tested method that just works, without drama. – cas Feb 14 '18 at 08:47
  • @cas there was "drama" when /var/www/ changed to /var/www/html, wasn't? – Arcticooling Feb 14 '18 at 08:53
  • Nope. The change only affected new installations. If you had an existing install of apache or nginx or whatever, your existing config would not have been changed. Anecdotally, I can say that I never even noticed the change (and i run several systems with web servers), I only read about it years after the fact. It was a non-event. If there was any controversy over it, that would be because people often get upset when defaults get changed, even if they don't affect them or are easy to override/ignore. Also, Debian are very, very good at not breaking systems when things get changed. – cas Feb 14 '18 at 08:59
  • mv ${pma}* / phpmyadmin/didn't do the job ? – Archemar Feb 14 '18 at 14:43
  • @Archemar it did. – Arcticooling Feb 14 '18 at 14:44
  • @muru, äh, sorry, perhaps I should have. – ilkkachu Feb 14 '18 at 15:28
  • 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
  • (Cont’d) …  and, somewhat ambiguously, the Sudoers Manual,  and the zip(1) and unzip(1) man pages. – Scott - Слава Україні Feb 18 '18 at 21:18
  • @Scott typo, meant to be "shell globs are not regexes", see original post in revision history – muru Feb 18 '18 at 23:55
  • 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 00:01

2 Answers2

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.
Kusalananda
  • 333,661
  • 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. – Arcticooling 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.

Michael Homer
  • 76,565