6
\$\begingroup\$

In the below script I am using some arrays, and other Bash stuff, but I have made a minimal working example for you, where you cannot see those things.

Anyway, I am interested in a review of the function find_gpg_key(). I haven't used gpg much so far to know if there is much simpler solution to filter out a (space-less) gpg key fingerprint. https://unix.stackexchange.com/q/335669/126755 indicates there may be differences in various gpg versions. Yet, I need it universal, if possible.

Edit #1:

To address OPTIONS: I don't understand why you're using.... I'm told, not all versions of gpg spill out the gpg key fingerprint by default. Which is why I must have used it, J_H. I haven't verified this theory though, doing to install some older Linux OS into VirtualBox I guess. Because you are correct, this is the sole reason the gpg output must be processed with sed.


#!/bin/bash
set -o nounset
readonly gpg_fingerprint='xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
function error()
{
 printf >&2 '%s\n' "$@"
 exit 1
}
function find_gpg_key()
{
 gpg --list-secret-keys --with-fingerprint | sed 's/ //g' | grep "$gpg_fingerprint" >/dev/null 2>&1
}
function gpg_encrypt()
{
 if ! find_gpg_key; then
 error "gpg key with fingerprint $gpg_fingerprint not found, aborting!"
 fi
 # do what you are supposed to when gpg key was found
}
asked Aug 29 at 9:35
\$\endgroup\$
1
  • \$\begingroup\$ Just a reminder that here on Code Review we prefer complete code rather than "a minimal working example". It's fine to show just a few functions from a larger project, if it's clear how they are to be used, but please don't "placeholder" code with comments, for example. \$\endgroup\$ Commented Aug 30 at 13:16

3 Answers 3

5
\$\begingroup\$

There's no need to list all secret keys and then filter them. Just pass the fingerprint as the key identifier:

find_gpg_key()
{
 gpg --list-secret-keys "$gpg_fingerprint" >/dev/null
}

GPG will exit with success status if the key exists, else produce a (valuable) message on standard error and exit with failure status.

As the man page says:

The best way to specify a key Id is by using the fingerprint.

Note also that most guides recommend using the portable syntax for function definition - there's no benefit to the Bash syntax with function keyword.

answered Aug 30 at 13:31
\$\endgroup\$
2
  • 1
    \$\begingroup\$ Kudos for quoting the man page. I lol'd. It is like the wisdom of the ancients has been pasted into the reality of the living. \$\endgroup\$ Commented Aug 30 at 18:34
  • \$\begingroup\$ @Vlastimil, My preference is to use portable constructs where possible, even in Bash scripts, because that makes it easier to reuse code in scripts for other shells. That's helpful if your work involves more than one shell. \$\endgroup\$ Commented Aug 31 at 7:02
7
\$\begingroup\$

The OP code passes shellcheck -- kudos! And it's nice that you've broken out helper functions with informative names.

options

I don't understand why you're using
gpg --list-secret-keys --with-fingerprint when the simpler
gpg --list-secret-keys would suffice. The only difference I notice is that --with-fingerprint inserts a SPACE character after each 32-bit grouping within the 40-nybble fingerprint.

I do not recommend using sed 's/ //g' to trim those blanks. Better that you just don't ask for them in the first place.

error suppression

... | grep "$gpg_fingerprint" >/dev/null 2>&1

I'm not crazy about that last part, as it's unclear why we need it. Please write a # comment explaining what edge case you're worried about. Generally, for an unexpected error, I'd rather see it than have it suppressed.

For gpg I'm quite willing to believe there's a whole raft of errors it could potentially display. Let's assume for the moment that we want to keep the end user in the dark about such errors. Notice that 2>&1 only affects the last pipeline stage, only affects the behavior of grep.

Let's use ( ) parens to run the pipeline within a child sub-shell:
( gpg ... | ... | grep ... ) > /dev/null 2>&1
Now we're discarding error messages from the whole pipeline, including gpg.

quiet matching

All you really care about is setting the exit status to SUCCESS (0) or NO_MATCH (1). Rather than dev null'ing undesired fingerprint output, consider just using the --quiet switch:
grep -q "$gpg_fingerprint"

And while we're on the topic of redirects, I'd prefer to spell
printf >&2 '%s\n' "$@" as
printf '%s\n' "$@" >&2. It's a matter of keeping app + app args together, followed by instructions to the shell.

One could offer redirection instructions to the shell before mentioning the app's name, it all works the same, but that just looks weird.

whitespace

Suppose we keep using sed.

The 3-part pipeline is nice enough the way it is. As a personal preference I would tend to break it up, one command per line, for readability.

 gpg --list-secret-keys --with-fingerprint |
 sed 's/ //g' |
 grep "$gpg_fingerprint" > /dev/null

And consider putting then on its own line, for natural indent support from emacs and other editors, so

 if ! find_gpg_key; then

becomes

 if ! find_gpg_key
 then

function name

find_gpg_key is a nice enough name, but we're not exactly "finding" or "looking up" a key. It's more about a "matching" boolean. (Plus, we're talking about a "fingerprint" rather than the bits of a secret "key".) So consider renaming to verify_fingerprint_exists, something along those lines.

input arg

readonly gpg_fingerprint='...'

Maybe that 40-char fingerprint is special, it belongs to a specific user, and mostly it doesn't change? In which case, please mention the userid as part of the identifier; gpg_ is a bit vague.

If this is really an input argment, then consider accepting it via an env var or via 1ドル.

answered Aug 29 at 16:13
\$\endgroup\$
0
4
\$\begingroup\$
gpg --list-secret-keys --with-fingerprint | sed 's/ //g' | grep "$gpg_fingerprint" >/dev/null 2>&1

Don't parse the meant-for-human output of gpg. The output is not reliable: it may change from version to version, locale to locale, one phase of the moon to the next, etc. Use the --with-colons option and parse its meant-for-machines output instead.

(You can also just pass the fingerprint on the command line, rather than searching the whole output for it, but this advice applies to any output of gpg you may want to interpret.)

References:

answered Sep 1 at 0:11
\$\endgroup\$
2
  • 1
    \$\begingroup\$ Welcome to Code Review (and to Stack Exchange)! Thanks for this great answer - I hope to see more from you in future! \$\endgroup\$ Commented Sep 1 at 9:19
  • 1
    \$\begingroup\$ Aha! You have found the git --porcelain option for this utility. Excellent. \$\endgroup\$ Commented Sep 1 at 17:52

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.