Replacing 'source file' with its content, and expanding variables, in bash
Asked Answered
F

1

6

In a script.sh,

source a.sh
source b.sh

CMD1
CMD2
CMD3

how can I replace the source *.sh with their content (without executing the commands)? I would like to see what the bash interpreter executes after sourcing the files and expanding all variables.

I know I can use set -n -v or run bash -n -v script.sh 2>output.sh, but that would not replace the source commands (and even less if a.sh or b.sh contain variables).

I thought of using a subshell, but that still doesn't expand the source lines. I tried a combination of set +n +v and set -n -v before and after the source lines, but that still does not work.

I'm going to send that output to a remote machine using ssh. I could use <<output.sh to pipe the content into the ssh command, but I can't log as root onto the remote machine, but I am however a sudoer. Therefore, I thought I could create the script and send it as a base64-encoded string (using that clever trick ) base64 script | ssh remotehost 'base64 -d | sudo bash'

Is there a solution? Or do you have a better idea?

Foozle answered 30/5, 2016 at 18:40 Comment(0)
S
6

You can do something like this:

inline.sh:

#!/usr/bin/env bash
while read line; do
    if [[ "$line" =~ (\.|source)\s+.+ ]]; then
        file="$(echo $line | cut -d' ' -f2)"
        echo "$(cat $file)"
    else
      echo "$line"
    fi
done < "$1"

Note this assumes the sourced files exist, and doesn't handle errors. You should also handle possible hashbangs. If the sourced files contain themselves source, you need to apply the script recursively, e.g. something like (not tested):

while egrep -q '^(source|\.)' main.sh; do
    bash inline.sh main.sh > main.sh
done

Let's test it

main.sh:

source a.sh
. b.sh

echo cc
echo "$var_a $var_b"

a.sh:

echo aa
var_a="stack" 

b.sh:

echo bb
var_b="overflow"

Result:

bash inline.sh main.sh

echo aa
var_a="stack"
echo bb
var_b="overflow"

echo cc
echo "$var_a $var_b"

bash inline.sh main.sh | bash

aa
bb
cc
stack overflow

BTW, if you just want to see what bash executes, you can run

bash -x [script]

or remotely

ssh user@host -t "bash -x [script]"
Soracco answered 30/5, 2016 at 20:20 Comment(4)
Have you tried your solution, when a.sh defines a variable, and main.sh calls it after 'echo cc'? I don't think that variable gets expanded. And yes, I know already about the -x. The script is meant to run on the remote machine, not mine, but I want to write it programmatically (ie with variables and some sort of includes)Vasilek
Updated the answer. The value doesn't need to be expanded, we just need its definition to be inlined (unless the sourced files contain themselves source). Why is there a difference between remote and local in this case? E.g. you can use ssh user@host -t "bash -x -c 'echo hi'"Soracco
I wrote an improved version that supports indentation and sourcing sources: gist.github.com/joehillen/30f08738c1c3c0ca3e4c754ad33ad2ffAlarmist
For anyone who is struggling with this, I've developed a POSIX script that is compatible with ShellCheck and completely customizable: github.com/carlocorradini/inlineRosenkranz

© 2022 - 2024 — McMap. All rights reserved.