Small app to show variables declared in a bash script 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' "$*" &gt;&amp;2; }

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

command_exists() { command -v "$1" &amp;&gt; /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 &lt; "$filename" | jq "$jq_query" | tr -d '"')
}

#------------------------------------------------------------------------------
(($#)) || {
  cat &lt;&lt; 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)
The difference between $* and $@


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



This is equivalent to the simpler:

warn() { echo "$*" &gt;&amp;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 &lt; "$filename" | jq "$jq_query" | tr -d '"')



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

shfmt -tojson &lt; "$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!