Solving “zsh: no matches found” When Using ‘*’ Characters

My Problem

When attempting to scp a directory full of files from a remote machine to a local machine I encountered the error “zsh: no matches found”.

scp user@remote-host:/remote/filesystem/* /local/filesystem
zsh: no matches found: user@remote-host:/remote/filesystem/*

My Solution

Escape the glob, either with a backslash, or quotes around the shell word that has the glob in it. In my example, a backslash would look like:

 scp user@remote-host:/remote/filesystem/\* /local/filesystem 

And quoting would look like:

 scp 'user@remote-host:/remote/filesystem/*' /local/filesystem 

You could also choose to use the noglob precommand modifier before the use of scp but that might not be useful if you’re using multiple globs and want one to actually expand locally. Nevertheless, this will work for simple uses:

noglob scp user@remote-host:/remote/filesystem/* /local/filesystem 

Bash Parameter Expansion: Variable Unset or Null ${YOU_BE_THE:-“Judge”}

I came across a lesser-used Bash idiom while attempting to implement ZeroSSL TLS certificates. Specifically, in ZeroSSL’s wrapper script to install their implementation of certbot. The idiom is seen here:


My interest was piqued by that one little dash in between CERTBOT_SCRIPT_LOCATION and "". To understand it, I’ll pull back and think about the whole line, component by component.


Let’s look at this line as if it was two sides of a seesaw with the middle being the = sign. The left half CERTBOT_SCRIPT_LOCATION= is simply a variable assignment. Whatever the right side of the = expands to is going to be put inside the variable CERTBOT_SCRIPT_LOCATION.

So far, so simple.

${ }

On the right side of the =, we have a dollar sign and a bunch of stuff within a pair of braces. Let’s ignore the content within the braces for now and examine the use of ${} as our next element.

The dollar sign character is interpreted by Bash to introduce a number of possible things, including command substitution, arithmetic evaluation, accessing the value of a named variable, or a parameter expansion.

Command substitution is triggered in Bash with $( and ends with a closing ). You could fill a variable with the return value of any command like this:

MY_VARIABLE=$( command )

Arithmetic substitution is triggered in bash with $(( and ends with a matching )). Whatever is between the double parentheses is expanded and treated as an arithmetic expression.

Variable values are accessed when a $ is followed by a named variable. You’ve already seen one named variable in this article: CERTBOT_SCRIPT_LOCATION. However, it currently has no value. In fact, as you read this, we’re currently in the midst of figuring out what value is going to be assigned to that variable.

Parameter expansion is introduced into bash with ${ and ending with a corresponding }. Any shell parameter found within the braces is expanded. There are a lot of arcane and esoteric shell parameters, but you’ve already been introduced to one type of shell parameter in this article: a variable. That’s right, shell variables are parameters. This brings us to the final piece of this puzzle.


We know that CERTBOT_SCRIPT_LOCATION is a variable and thus a shell parameter, so Bash will attempt to expand it within the ${} construct. However, we’re pretty sure that it’s empty at this point. And what’s with the double-quoted string that contains a URL? And why is a dash separating them?! That lowly dash is the linchpin that holds all of this together.

Within a parameter expansion expression, the dash will test if the variable on the left is set or not. If it is set, the variable is expanded and what’s on the right is discarded.

However, if the parameter on the left of the dash is not set, then the thing on the right side of the dash is expanded (if it needs to be) and then assigned as the value of the variable on the left of the dash. Let’s take a look at our specimen:


The above says, in plain language: “Does the variable CERTBOT_SCRIPT_LOCATION exist? If it does, return the variable’s value. If the variable doesn’t exist, then insert the string "" into it, and finally return that value.

Putting it all Together

Whew! We’ve been through a lot, but there’s still a bit more to go. Let’s take a look at the whole line again, explain what’s happening, and then put it in context:


We’re creating a variable named CERTBOT_SCRIPT_LOCATION and assigning it the final value of the parameter expansion on the right side of the = sign.

Within that parameter expansion expression, we’re checking if CERTBOT_SCRIPT_LOCATION already exists. If it does, return the value of that variable which is immediately assigned to that exact same variable. This looks a little weird, but it’s a Bash idiom that means “If CERTBOT_SCRIPT_LOCATION already exists, leave it alone.”

However, if the variable CERTBOT_SCRIPT_LOCATION does not exist, then create it and put the string "" inside.

To put things into greater context, that variable is later used within a call to curl:


The question you may now be asking is: “Why?!” Why not avoid the use of a seldom used, single character test that took so long to explain? Why not use curl and supply the URL directly? Without asking the script author, here are three reasons that I think the script was written this way:

Abstraction. We use variables for any information that has a reasonable chance of being changed. A URL can easily change, and if we assign it to a variable once, we can more easily change that value at a later date. We never need to worry about changing the URL in every spot that we used it.

Documentation. When you assign a value to a variable, you name the variable. In this case, our value is a URL. What exactly does that URL do? What is its purpose? When we assign the URL to a variable named CERTBOT_SCRIPT_LOCATION, now we have an explanation. Every time we use that variable it reminds us of what it’s doing.

Safety. The two reasons above explain the use of variables, but not that lone dash. I believe the dash idiom was chosen for safety. Maybe we ran the script multiple times before, or perhaps something else set it previously. We don’t need to keep repeating the process of setting the variable, and if it was set previously, let’s not overwrite it.

Final Thoughts

I noticed that the script does not check CERTBOT_SCRIPT_LOCATION for a value that makes sense. What if it’s set, but has a number in it? Or a string that isn’t an HTTP URL? Those are more complex problems. How would you solve them?

In the title of this article, I used a slightly different bash idiom: the use of :- rather than the lonesome -. If we look to Bash’s documentation, we find:

When not performing substring expansion, using the form described below (e.g., ‘:-’), Bash tests for a parameter that is unset or null. Omitting the colon results in a test only for a parameter that is unset.

The dash merely checks for the existence of the variable on its left. The colon-dash will additionally check if the variable exists but is null. If the value is null, then Bash assigns the value on the right to the variable. Ask yourself which logic makes the most sense for your own scripts.

Do you have any scripts you’d like me to tear down? Any shell idioms that you’re not sure about? Comment below!

Adding Simple base64 Decoding to Your Shell

I had a need to repeatedly decode some base64 strings quickly and easily. Easier than typing out openssl base64 -d -in -out, or even base64 --decode file.

The simplest solution that I found and prefer is a shell function with a here string. Crack open your preferred shell’s profile file. In my case, .zshrc. Make a shell function thusly:

decode() {
  base64 --decode <<<$1

Depending on your shell and any addons, you may need to echo an extra newline to make the decoded text appear on its own line and not have the next shell prompt append to the decoded text.