Bash script autobeautifiying using declare -f

bashindentationshell-script

I am trying to "beautify" scripts automatically by using declare -f function

In a nutshell: Given a bash script foo , you can apparently "automagically" indent it by doing, in bash:
MAGIC () { ...here you copy/paste the whole foo script... ; } ; declare -f MAGIC

I provide an example script below that does that for any script you give it as input.

My question is : what are the main caveats of using an encompassing function and then declare -f it ?

  • any special dangers?
  • limit on the number of function nesting ? (I can have functions in the script, they are happily indented as well, but how many levels deep can I go?)
  • "won't be able to do this/interpret that/…" ? (it seems awk script get fine, and are even indented a bit (the main script is aligned to the rest of the script) … but what (other) things could pose problems?
  • will lose some informations? I noticed: it removes blank lines, and comments (# ...), but what else does it discard/modify?

I want to get your input before I can use my "indent" script below to reindent a bunch of bash scripts…

Here is my first attempt, comments (and answers about concerns above) welcomed :

#!/usr/bin/bash
 # parameters:  $@ = bash (only) scripts that need reindentations.

for script in "$@" ; do
  tmpfile="${script}.BEAUTIFY.tmp"
  finalfile="${script}.BEAUTIFY"
  # optionnal temporisation...
  echo "I am about to create ${script}.BEAUTIFY from the bash (I hope) script: ${script} . Press return if OK, ctrl-C if not"
  read temporisation
  # we output the following commands into a pipe to a subshell bash:
  { sleep 1
    echo "
      # declare surrounding function . The script has to be bash, but can contain also functions, etc.
      # Will lose # comments and blank lines, but maybe also other things... hence the Question on unix.se
      ODBEAUTIFIER () {
      $(cat "$script")
      }
      #  print if via declare -f, to pretiffy it
      declare -f ODBEAUTIFIER > \"${tmpfile}\"
      #  then get rid of the surrounding ODBEAUTIFIER function:
      tail -n +3 \"${tmpfile}\" | tac | tail -n +2 | tac | sed -e 's/    //' > \"${finalfile}\"
      #  and displays the resulting file
      ls -l \"${script}\" \"${finalfile}\"
      \rm \"${tmpfile}\" ; # that rm has a backslash to avoid aliases to rm -i
      "
   } | bash ; #  that sub- bash receives the input and interprets it
done

Note/edit: There is a workaround (but with many caveats and dangers) to avoid the "declare -f will lose comments starting with #": but it seems it distracts (… at least one insisting user, see comments below ^^) from the main question (Even tough I cited the many caveats of the workaround, I prefer to remove its mention above as it wasn't at all the point of the question. Curious minds may refer to the edit before now ^^ )

Best Answer

There's a security risk with reading in the script as a function then piping to another shell: If the input file contains an unmatched }, then whatever follows it will be executed (and omitted from the output).

An unmatched } could come about accidentally or maliciously. It would normally cause bash to report a syntax error, but an attacker could hide this (from the user running the beautify script) by ending the file with another, unfinished function:

}
echo "insert shellcode here" > ~/RCFILE
function end_func {
    true

Less important:

  • You've mentioned that it loses comments starting with #. That includes the opening #!/bin/..., so it may prevent the output files from running.

  • sed -e 's/ //' strips the first 4 spaces in any line. Those are usually the 4 spaces prepended by declare. But declare doesn't insert spaces before the lines of a here-document, so this may delete other spaces that were part of the original.

  • You could use head -n -1 instead of tac | tail -n +2 | tac. Or you could remove both tails and use sed '1,2d;$d'.

Related Question