6
\$\begingroup\$

This code and updates are available on Github.

I've written a small utility to dump the variables that are assigned in a bash script.

Please note:

  • This script depends on the shfmt and jq apps.
  • I am not looking to make this script posix compliant, only bash specific.

Some possible areas for improvement:

  • Can the jq query be improved?
  • Are there other entries in the json output of shfmt that I can look for variable assignments.
  • Any gotchas I'm missing?

As I type out this question, it occurs to me that showing variables used in a script would be a good thing to allow as an option. I'll look into it, but if anyone has any ideas in this respect, I'm interested in hearing your input.

#!/bin/bash

#------------------------------------------------------------------------------

warn() { printf '%s\n' "$*" >&2; }

die() {
  (($#)) && warn "$*"
  exit 1
}

command_exists() { command -v "$1" &> /dev/null; }

#------------------------------------------------------------------------------
_showvars() {
  local filename=$1
  [[ -f $filename ]] || die "$filename is not a file or does not exist."
  [[ -r $filename ]] || die "$filename is not readable."

  jq_query='[ .. | select(.Assigns?) | .. | select(.Name?) | .Name.Value ] | unique[]'

  # shellcheck disable=SC2046
  printf '  %s\n' $(shfmt -tojson < "$filename" | jq "$jq_query" | tr -d '"')
}

#------------------------------------------------------------------------------
(($#)) || {
  cat << EOH

showvars is a simple script that shows
what variables are assigned in a bash
script

usage: showvars filename [filename ...]

EOH

  exit 1
}

for r in shfmt jq; do
  command_exists $r || die This script depends on $r and it is not found.
done

for f in "$@"; do
  printf '\n%s:\n' "$f"
  _showvars "$f"
  shift
done

echo

Edit: Fixed copy-paste error with (($#))... line Edit: Removed code that removes lower case variables (an artifact from another code solution)

\$\endgroup\$
1

1 Answer 1

2
\$\begingroup\$

The difference between $* and $@

warn() { printf '%s\n' "$*" >&2; }

This is equivalent to the simpler:

warn() { echo "$*" >&2; }

The printf version is useful if you want to produce one line per parameter, and in that case you must use "$@" instead of "$*". Also in callers of warn.

Use -r for raw output of jq

Instead of jq "..." | tr -d '"' a better way is jq -r "...".

An alternative to printf and a sub-shell

Instead of this:

  # shellcheck disable=SC2046
  printf '  %s\n' $(shfmt -tojson < "$filename" | jq "$jq_query" | tr -d '"')

I recommend this way (and no need to disable shellcheck):

shfmt -tojson < "$filename" | jq -r "$jq_query" | sed -e 's/^/  /'

Use a bit more double-quotes

You did a good job of double-quoting the most important things. I would double-quote here too:

command_exists $r || die This script depends on $r and it is not found.

To train good habits:

command_exists "$r" || die "This script depends on $r and it is not found."

"$@" is the default list for for

Instead of for f in "$@"; do, you can simply write for f; do.

The shebang

In some systems Bash is not in /bin/bash. For that reason I prefer to use #!/usr/bin/env bash as the shebang, it makes the script more portable.

Simplify the readable file check?

  [[ -f $filename ]] || die "$filename is not a file or does not exist."
  [[ -r $filename ]] || die "$filename is not readable."

The -r implies -f. I would simplify this to one line:

[[ -r $filename ]] || die "$filename is not a readable file."

Use echo when it's good enough

Instead of printf '\n%s:\n' "$f" I would write:

echo
echo "$f:"

Here-documents

EOH is an unusual symbol for the here-document marker. That's not a problem, but I think the less surprising elements in a script, the better. I don't see a good reason to not call this EOF as usual.

Your questions

Some possible areas for improvement:

  • Can the jq query be improved?
  • Are there other entries in the json output of shfmt that I can look for variable assignments.

Unfortunately I'm not able to answer these. You might want to wait for another reviewer who can!

\$\endgroup\$
1
  • \$\begingroup\$ Thank you. I implemented some of your suggestions, while others prompted a different change. I've included details in my question. \$\endgroup\$
    – harleypig
    Aug 1, 2019 at 18:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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