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
}
-
\$\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\$Toby Speight– Toby Speight2025年08月30日 13:16:34 +00:00Commented Aug 30 at 13:16
3 Answers 3
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.
-
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\$chicks– chicks2025年08月30日 18:34:14 +00:00Commented 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\$Toby Speight– Toby Speight2025年08月31日 07:02:20 +00:00Commented Aug 31 at 7:02
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ドル
.
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:
- gpg man page; search for
--with-colons
- doc/DETAILS documentation of the output format
-
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\$Toby Speight– Toby Speight2025年09月01日 09:19:09 +00:00Commented Sep 1 at 9:19
-
1\$\begingroup\$ Aha! You have found the git
--porcelain
option for this utility. Excellent. \$\endgroup\$J_H– J_H2025年09月01日 17:52:48 +00:00Commented Sep 1 at 17:52