Reputation: 8064
I am writing a simple bash debugging tool, log_next_line
. The intended behavior is when the log
variable is defined it reads next line of the script, expand variables on it, writes it to the file specified by $log
, executes the command, and capture its output to the same log.
I have the problem with the variable expansion. Suppose I have a line of bash code in variable $line
. How to expand the strings in it without executing any external programs, and without executing pipes (which is dangerous)?
The simple line=$("echo $line")
doesn't work at all.
When I try line=$(bash -c "echo $line")
I have better luck, but then I need to export ALL bash variables to the spawned bash (which I have no idea how to do). And calling the external program for it seems like a overkill; besides the bash will execute the pipes and external programs, if e.g. line="echo $(rm -r /)"
.
Is there any way to do the variable expansion that will not involve writing a Bash parser from scratch ;-) ? I only need it to work under Linux (Ubuntu >= 14.04 to be precise).
For a full picture I include the prototype of the function:
function log_next_line()
{
if [ -n "$log" ]; then
file=${BASH_SOURCE[1]##*/} #Takes path to the file, from where the function is called
linenr=$((${BASH_LINENO[0]} + 1 )) #Line number
line=`sed "${linenr}q;d" $file` #Reads this line number from the source file
line=$("echo $line") #DOESN'T WORK. I want it to do a variable expansion
echo "\$ $line" >>$log #Writes the variable-expanded line to the log
$line >>$log 2>>$log #Executes the line and
exitstatus=$?
if [ "$exitstatus" -ne "0" ]; then
echo "## Exit status: $exitstatus" >>$log
fi
echo >>$log
BASH_LINENO[0]=$((BASH_LINENO[0] + 1)) #Skips executing of the next line, since we have already executed it (and captured its output)
if [ "$exitstatus" -ne "0" ]; then
exit $exitstatus
fi
fi
}
And the simple script that can be used as a test case
#!/bin/bash
log=mylog.log
. ./log_next_line.sh
rm mylog.log
a=4
b=example
x=a
log_next_line
echo $x
log_next_line
echo ${!b}
log_next_line
touch ${!b}.txt > /dev/null
log_next_line
touch ${!x}.txt
log_next_line
if [ (( ${#a} - 6 )) -gt 10 ]; then echo "Too long string";fi
log_next_line
echo "\$a and \$x"|tee file.txt
unit tests:
if x=a
, a="example"
then I want the following expansions:
echo $x
should be echo a
. echo ${!b}
should be echo example
touch ${!b}.txt>/dev/null
should be touch example.txt>/dev/null
if [ (( ${#a} - 6 )) -gt 10 ]; then echo "Too long string";fi
should be if [ 1 -gt 10 ]; then echo "Too long string";fi
echo "\$a and \$x"|tee file.txt
should be echo "\$a and \$x"|tee file.txt"
This question is a generalization of the https://unix.stackexchange.com/questions/131150/bash-is-it-safe-to-eval-bash-command . The answers given there don't pass all my test cases.
Upvotes: 3
Views: 349
Reputation: 17455
the function could be implemented as a simple set -x
to turn on tracing at a given point of a script and set +x
to turn it off.
If you definitely wish to implement it as a one line flag, I would take a look at this question and configure -x
flag through DEBUG trap.
Upvotes: 1