Shell – Flatten shell script (include sourced scripts) and remove unused functions

debianinit-scriptshell

I am manually tweaking Debian init scripts in order to optimize the boot time of an embedded device. So far I reduced it by half just with the "low hanging fruits" ie. the smaller scripts that are easy to understand.

Now I am left with a few init scripts that take about 20s to run in total. Based on the experience from the other scripts, I can gain another 5 to 10 seconds here. The problem is those scripts are a bit too hard for me to understand because they source several helper scripts from /lib/init/ which are difficult to follow (mount-functions.sh being the first culprit).

In order to make those remaining scripts easier to understand so that I can optimize them, I'd like some way to "pre-process" those init scripts so that whenever they source a file it gets inlined in the script itself (recursively of course), and remove the unused functions to unclutter the code. Is there a tool for this?

Best Answer

You could do something like this. I've only lightly tested it but I think the general concept is sound.

Example

Say you have the following directory of files:

$ ls -l
total 16
-rwxrwxr-x 1 saml saml 268 Oct  4 17:44 expander.bash
-rw-rw-r-- 1 saml saml  18 Oct  4 16:49 src_1.bash
-rw-rw-r-- 1 saml saml  20 Oct  4 16:50 src_2.bash
-rwxrwxr-x 1 saml saml 119 Oct  4 16:49 top.bash

The top.bash file looks like this:

$ cat top.bash 
#!/bin/bash

echo "1"
echo "2"
echo "3"
echo "4"

. src_1.bash

echo "15"
echo "16"

. src_2.bash

echo "27"
echo "28"

You could use the following script, expander.bash to "expand" top.bash:

#!/bin/bash

while read -r line; do
  if echo "$line" | grep -q '\. .*'; then
    file=$(echo "$line" | sed 's/\. //')
    echo "### below sourced from: $file"
    cat $file
    echo "### above sourced from: $file"
    continue
  fi
  printf "%s\n" "$line"
done < top.bash

Example Run

$ ./expander.bash 
#!/bin/bash

echo "1"
echo "2"
echo "3"
echo "4"

### below sourced from: src_1.bash
echo "6"
echo "7"
### above sourced from: src_1.bash

echo "15"
echo "16"

### below sourced from: src_2.bash
echo "17"
echo "18"
### above sourced from: src_2.bash

echo "27"
echo "28"

Potential Enhancements?

For one I used grep and sed, those could be swapped out to make it a more pure Bash solution. I didn't take the time to do this, since this is a rough prototype.

The second area that will most likely need attention is the scripts ability to figure out that a sourcing of another file is occurring. The pattern that detects this will likely need to be "tweaked" based on your situation.