# 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.

• 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) ## 1 Answer ### 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."


### 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.